diff --git a/CODEOWNERS b/CODEOWNERS
index 6589d06defe52..bf03696e65220 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -91,6 +91,7 @@
/bundles/org.openhab.binding.folding/ @fa2k
/bundles/org.openhab.binding.foobot/ @airboxlab @Hilbrand
/bundles/org.openhab.binding.freebox/ @lolodomo
+/bundles/org.openhab.binding.freeboxos/ @clinique
/bundles/org.openhab.binding.fronius/ @trokohl
/bundles/org.openhab.binding.fsinternetradio/ @paphko
/bundles/org.openhab.binding.ftpupload/ @paulianttila
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index 54c6f8c3dcdb3..ddc369a0d022d 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -441,6 +441,11 @@
org.openhab.binding.freebox
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.freeboxos
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.fronius
diff --git a/bundles/org.openhab.binding.freeboxos/NOTICE b/bundles/org.openhab.binding.freeboxos/NOTICE
new file mode 100644
index 0000000000000..edfd204e5d248
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the openHAB project.
+
+* Project home: https://www.openhab.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/openhab/openhab-addons
diff --git a/bundles/org.openhab.binding.freeboxos/README.md b/bundles/org.openhab.binding.freeboxos/README.md
new file mode 100644
index 0000000000000..5473d95c9c9b4
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/README.md
@@ -0,0 +1,172 @@
+# Freebox Binding
+
+This binding integrates the [Freebox Revolution](https://www.free.fr/freebox/freebox-revolution/) and [Freebox Delta](https://www.free.fr/freebox/freebox-delta/) to your openHAB installation.
+
+The server can be connected to Free infrastructures either by xDSL or fiber optic.
+
+## Supported Things
+
+This binding supports the following thing types:
+
+| Thing | Thing Type | Description |
+|------------|------------|-------------------------------------------------------|
+| api | Bridge | Bridge to access freebox OS API hosted by the server. |
+| delta | Thing | A Freebox Delta server. |
+| revolution | Thing | A Freebox Revolution server. |
+| player | Thing | The TV player equipment (e.g. Devialet). |
+| landline | Thing | The phone wired to the Freebox Server. |
+| host | Thing | A network device on the local network. |
+| wifihost | Thing | A wifi networked device on the local network. |
+| vm | Thing | A virtual machine hosted on the server (>= Delta). |
+| repeater | Thing | A Free wifi repeater. |
+
+## Discovery
+
+The API bridge is discovered automatically through mDNS in the local network.
+After the bridge is discovered and available to openHAB, the binding will automatically discover phone, network devices available on the local network.
+Note that the discovered thing will be setup to use only HTTP API (and not HTTPS) because only an IP can be automatically determined while a domain name is required to use HTTPS with a valid certificate.
+
+## Binding configuration
+
+The binding itself is not configurable.
+
+## Thing Configuration
+
+### Api bridge
+
+| Parameter Label | Parameter ID | Description | Required | Default |
+|--------------------------|-------------------|--------------------------------------------------------|----------|----------------------|
+| Freebox Server Address | api_domain | The domain to use in place of hardcoded Freebox ip | No | mafreebox.freebox.fr |
+| Application Token | appToken | Token generated by the Freebox Server. | Yes | |
+| Network Device Discovery | discoverNetDevice | Enable the discovery of network device things. | No | false |
+| HTTPS Available | https_available | Tells if https has been configured on the Freebox | No | false |
+| HTTPS port | https_port | Port to use for remote https access to the Freebox Api | No | 15682 |
+
+If the parameter *api_domain* is not set, the binding will use the default address used by Free to access your Freebox Server (mafreebox.freebox.fr).
+The bridge thing will initialize only if a valid application token (parameter *appToken*) is filled.
+
+### Server : Revolution or Delta
+
+The *revolution* or *delta* thing requires the following configuration parameters:
+
+| Parameter Label | Parameter ID | Description | Required | Default |
+|------------------|-----------------|--------------------------------------------------------------------------|----------|---------|
+| Refresh Interval | refreshInterval | The refresh interval (seconds) which is used to poll the Freebox Server. | No | 30 |
+
+### Player thing
+
+The *player* thing requires the following configuration parameters:
+
+| Parameter Label | Parameter ID | Description | Required | Default |
+|------------------|-----------------|----------------------------------------------------------------------------|----------|---------|
+| MAC Address | macAddress | The MAC address of the player device. | Yes | |
+| ID | id | Id of the player within Freebox Api | Yes | 1 |
+| Player port | port | | No | 24322 |
+| Password | password | AirPlay password | No | |
+| Remote Code | remoteCode | Code associated to remote control | No | |
+| Accept all MP3 | acceptAllMp3 | Accept any bitrate for MP3 audio or only bitrates greater than 64 kbps | No | true |
+| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll the player | Yes | 30 |
+| Callback URL | callbackUrl | URL to use for playing notification sounds, e.g. 'http://192.168.0.2:8080' | No | |
+
+### Landline
+
+The *landline* thing requires the following configuration parameters:
+
+| Parameter Label | Parameter ID | Description | Required | Default |
+|------------------|-----------------|------------------------------------------------------------------------|----------|---------|
+| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll for phone state. | No | 2 |
+
+### Network devices : Host and WifiHost
+
+The *host* and *wifihost* things requires the following configuration parameters:
+
+| Parameter Label | Parameter ID | Description | Required | Default |
+|------------------|-----------------|----------------------------------------------------------------------------|----------|---------|
+| MAC Address | macAddress | The MAC address of the network host . | Yes | |
+| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll for phone state. | No | 30 |
+
+### Repeater and Vm thing
+
+The *repeater* thing is a specialized case of a *wifihost*. The *vm* derives from *host*. They share the same configuration definition :
+
+| Parameter Label | Parameter ID | Description | Required | Default |
+|------------------|-----------------|------------------------------------------------------------------|----------|---------|
+| MAC Address | macAddress | The MAC address of the player device. | No | |
+| ID | id | Id of the repeater within Freebox Api | Yes | 1 |
+| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll the player | Yes | 30 |
+
+## Authentication
+
+You will have to authorize openHAB to connect to your Freebox. Here is the process described :
+
+**Step 1** At binding startup, if no token is recorded in the Freebox Server (bridge) configuration, the binding will run a pairing request
+
+**Step 2** Run to your Freebox and approve the pairing request for openHAB Freebox Binding that is displayed on the Freebox screen
+
+**Step 3** the application token is automatically recorded in the Freebox Server (bridge) configuration
+
+**Step 4** you can use the console command `freeboxos apptoken` to display the application token and use it later to set up your thing in a configuration file
+
+**Optionally** you can log in your Freebox admin console to allocate needed rights to openHAB
+
+Once initialized, the thing will generate all available channels.
+
+## Channels
+
+The following channels are supported:
+
+| Thing | Channel Type ID | Item Type | Access Mode | Description |
+|---------------|----------------------|-----------|-------------|--------------------------------------------------------------------------------|
+| revolution | lcd_brightness | Number | RW | Brightness level of the screen in percent |
+| revolution | lcd_orientation | Number | RW | Screen Orientation in degrees (0 or 90 or 180 or 270) |
+| revolution | lcd_forced | Switch | RW | Indicates whether the screen orientation forced |
+| server (*) | uptime | Number | R | Time since last reboot of the Freebox Server |
+| server (*) | restarted | Switch | R | Indicates whether the Freebox server has restarted during the last poll period |
+| server (*) | wifi_status | Switch | RW | Indicates whether the WiFi network is enabled |
+| server (*) | ftp_status | Switch | RW | Indicates whether the FTP server is enabled |
+| server (*) | airmedia_status | Switch | RW | Indicates whether Air Media is enabled |
+| server (*) | upnpav_status | Switch | RW | Indicates whether UPnP AV is enabled |
+| server (*) | samba-file-status | Switch | RW | Indicates whether Window File Sharing is enabled |
+| server (*) | samba-printer-status | Switch | RW | Indicates whether Window Printer Sharing is enabled |
+| server (*) | xdsl_status | String | R | Status of the xDSL line |
+| server (*) | ftth_status | Switch | R | Status of the Ftth line |
+| server (*) | line_status | String | R | Status of network line connexion |
+| server (*) | ipv4 | String | R | Public IP Address of the Freebox Server |
+| server (*) | rate_up | Number | R | Current upload rate in byte/s |
+| server (*) | rate_down | Number | R | Current download rate in byte/s |
+| server (*) | bytes_up | Number | R | Total uploaded bytes since last connection |
+| server (*) | bytes_down | Number | R | Total downloaded bytes since last connection |
+| phone | state#onhook | Switch | R | Indicates whether the phone is on hook |
+| phone | state#ringing | Switch | R | Is the phone ringing |
+| phone | accepted#number | Call | R | Last accepted call: number |
+| phone | accepted#duration | Number | R | Last accepted call: duration in seconds |
+| phone | accepted#timestamp | DateTime | R | Last accepted call: creation timestamp |
+| phone | accepted#name | String | R | Last accepted call: caller name |
+| phone | missed#number | Call | R | Last missed call: number |
+| phone | missed#timestamp | DateTime | R | Last missed call: creation timestamp |
+| phone | missed#name | String | R | Last missed call: caller name |
+| phone | outgoing#number | Call | R | Last outgoing call: number |
+| phone | outgoing#duration | Number | R | Last outgoing call: duration in seconds |
+| phone | outgoing#timestamp | DateTime | R | Last outgoing call: creation timestamp |
+| phone | outgoing#name | String | R | Last outgoing call: called name |
+| net_device | reachable | Switch | R | Indicates whether the network device is reachable |
+| net_interface | reachable | Switch | R | Indicates whether the network interface is reachable |
+| airplay | playurl | String | W | Play an audio or video media from the given URL |
+| airplay | stop | Switch | W | Stop the media playback |
+
+(*) : server means *delta* or *revolution*
+
+## Actions for ules
+
+The following actions are available in rules/scripting:
+
+| Thing Type | Action Name | Description |
+|-------------|------------------|------------------------------------------------------|
+| host | wol | Sends a wake on lan packet to the lan connected host |
+| player | reboot | Reboots the player device. |
+| player | sendKey | Send a key (remote emulation) to the player |
+| player | sendLongKey | Sends the key emulating a longpress on the button |
+| player | sendMultipleKeys | Sends multiple keys to the player, comma separated |
+| player | sendKeyRepeat | Sends the key multiple times |
+| server | reboot | Reboots the Freebox Server. |
+
diff --git a/bundles/org.openhab.binding.freeboxos/pom.xml b/bundles/org.openhab.binding.freeboxos/pom.xml
new file mode 100644
index 0000000000000..13821553d0476
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 3.2.0-SNAPSHOT
+
+
+ org.openhab.binding.freeboxos
+
+ openHAB Add-ons :: Bundles :: FreeboxOS Binding
+
+
+
+ org.hibernate.validator
+ hibernate-validator
+ 6.2.0.Final
+
+
+ com.thoughtworks.paranamer
+ paranamer
+ 2.8
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/feature/feature.xml b/bundles/org.openhab.binding.freeboxos/src/main/feature/feature.xml
new file mode 100644
index 0000000000000..5475c6cf2998b
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/feature/feature.xml
@@ -0,0 +1,14 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ openhab-transport-mdns
+ mvn:com.fasterxml/classmate/1.5.1
+ mvn:javax.money/money-api/1.0.1
+ mvn:jakarta.persistence/jakarta.persistence-api/2.2.3
+ mvn:joda-time/joda-time/2.9.7
+ mvn:org.openhab.addons.bundles/org.openhab.binding.freeboxos/${project.version}
+
+
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
new file mode 100644
index 0000000000000..9a96f4d85d0ce
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/FreeboxOsBindingConstants.java
@@ -0,0 +1,133 @@
+/**
+ * Copyright (c) 2010-2021 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;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.OpenClosedType;
+import org.openhab.core.library.types.UpDownType;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.types.Command;
+
+/**
+ * The {@link FreeboxBinding} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class FreeboxOsBindingConstants {
+
+ public static final String BINDING_ID = "freeboxos";
+
+ // List of all Bridge Type UIDs
+ public static final ThingTypeUID BRIDGE_TYPE_API = new ThingTypeUID(BINDING_ID, "api");
+
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID THING_TYPE_REVOLUTION = new ThingTypeUID(BINDING_ID, "revolution");
+ public static final ThingTypeUID THING_TYPE_DELTA = new ThingTypeUID(BINDING_ID, "delta");
+ public static final ThingTypeUID THING_TYPE_LANDLINE = new ThingTypeUID(BINDING_ID, "landline");
+ public static final ThingTypeUID THING_TYPE_HOST = new ThingTypeUID(BINDING_ID, "host");
+ public static final ThingTypeUID THING_TYPE_WIFI_HOST = new ThingTypeUID(BINDING_ID, "wifihost");
+ public static final ThingTypeUID THING_TYPE_PLAYER = new ThingTypeUID(BINDING_ID, "player");
+ public static final ThingTypeUID THING_TYPE_ACTIVE_PLAYER = new ThingTypeUID(BINDING_ID, "active_player");
+ public static final ThingTypeUID THING_TYPE_VM = new ThingTypeUID(BINDING_ID, "vm");
+ public static final ThingTypeUID THING_TYPE_REPEATER = new ThingTypeUID(BINDING_ID, "repeater");
+
+ // All supported Thing types
+ public static final Set BRIDGE_TYPE_UID = Set.of(BRIDGE_TYPE_API);
+ public static final Set THINGS_TYPES_UIDS = Set.of(THING_TYPE_LANDLINE, 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);
+
+ protected static final Set SUPPORTED_THING_TYPES_UIDS = Stream
+ .concat(BRIDGE_TYPE_UID.stream(), THINGS_TYPES_UIDS.stream()).collect(Collectors.toSet());
+
+ // Thing properties
+ public static final String PHONE_TYPE = "Phone Type";
+
+ // List of all Group Channel ids
+ public static final String GROUP_SENSORS = "sensors";
+ public static final String CONNECTION_STATUS = "connection-status";
+ public static final String SYS_INFO = "sysinfo";
+ public static final String ACTIONS = "actions";
+ public static final String FILE_SHARING = "file-sharing";
+ public static final String CONNECTIVITY = "connectivity";
+ public static final String STATE = "state";
+ public static final String DISPLAY = "display";
+ public static final String VM_STATUS = "vmstatus";
+ public static final String GROUP_WIFI = "wifi";
+ public static final String REPEATER_MISC = "repeater-misc";
+ public static final String PHONE_MISC = "phone-misc";
+
+ // List of all Channel ids
+ public static final String RSSI = "rssi";
+ public static final String SSID = "ssid";
+ public static final String WIFI_QUALITY = "wifi-quality";
+ public static final String WIFI_HOST = "wifi-host";
+ public static final String UPTIME = "uptime";
+ public static final String BOX_EVENT = "box-event";
+ public static final String PHONE_EVENT = "phone-event";
+ public static final String LCD_BRIGHTNESS = "lcd-brightness";
+ public static final String LCD_ORIENTATION = "lcd-orientation";
+ public static final String LCD_FORCED = "lcd-forced";
+ public static final String WIFI_STATUS = "wifi-status";
+ public static final String IP_ADDRESS = "ip-address";
+ public static final String LINE_STATUS = "line-status";
+ public static final String PLAYER_STATUS = "player-status";
+ public static final String RATE_UP = "rate-up";
+ public static final String RATE_DOWN = "rate-down";
+ public static final String BYTES_UP = "bytes-up";
+ public static final String BYTES_DOWN = "bytes-down";
+ public static final String PCT_BW_UP = "bandwidth-usage-up";
+ public static final String PCT_BW_DOWN = "bandwidth-usage-down";
+ public static final String ONHOOK = "onhook";
+ public static final String RINGING = "ringing";
+
+ public static final String FTP_STATUS = "ftp-status";
+ public static final String SAMBA_FILE_STATUS = "samba-file-status";
+ public static final String SAMBA_PRINTER_STATUS = "samba-printer-status";
+ public static final String REACHABLE = "reachable";
+ public static final String LAST_SEEN = "last-seen";
+ public static final String ALTERNATE_RING = "lcd-forced";
+ public static final String DECT_ACTIVE = "dect-active";
+ public static final String UPNPAV_STATUS = "upnpav-status";
+
+ // Call channels for groups Accepted, Missed and Outgoing
+ public static final String NUMBER = "number";
+ public static final String DURATION = "duration";
+ public static final String TIMESTAMP = "timestamp";
+ public static final String NAME = "name";
+
+ // Freebox player channels
+ public static final String AIRMEDIA_STATUS = "airmedia-status";
+ public static final String KEY_CODE = "key-code";
+
+ // Virtual machine channels
+ public static final String STATUS = "status";
+
+ // Repeater channels
+ public static final String LED = "led";
+ public static final String HOST_COUNT = "host-count";
+ public static final String RPT_TIMESTAMP = "start-timestamp";
+
+ // Defaults api strings
+ public static final String DEFAULT_FREEBOX_NAME = "mafreebox.freebox.fr";
+
+ 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
new file mode 100644
index 0000000000000..6fa3d9c15a276
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/FreeboxOsHandlerFactory.java
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2010-2021 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;
+
+import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
+
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
+import org.openhab.binding.freeboxos.internal.api.ApiHandler;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.handler.ActivePlayerHandler;
+import org.openhab.binding.freeboxos.internal.handler.FreeboxOsHandler;
+import org.openhab.binding.freeboxos.internal.handler.HostHandler;
+import org.openhab.binding.freeboxos.internal.handler.LandlineHandler;
+import org.openhab.binding.freeboxos.internal.handler.PlayerHandler;
+import org.openhab.binding.freeboxos.internal.handler.RepeaterHandler;
+import org.openhab.binding.freeboxos.internal.handler.RevolutionHandler;
+import org.openhab.binding.freeboxos.internal.handler.ServerHandler;
+import org.openhab.binding.freeboxos.internal.handler.VmHandler;
+import org.openhab.binding.freeboxos.internal.handler.WifiHostHandler;
+import org.openhab.core.audio.AudioHTTPServer;
+import org.openhab.core.net.NetworkAddressService;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+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.Reference;
+
+/**
+ * The {@link FreeboxOsHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+@Component(service = ThingHandlerFactory.class, configurationPid = "binding.freeboxos")
+public class FreeboxOsHandlerFactory extends BaseThingHandlerFactory {
+ private final AudioHTTPServer audioHTTPServer;
+ private final ApiHandler apiHandler;
+ private final Validator validator;
+ private final @Nullable String ipAddress;
+
+ @Activate
+ public FreeboxOsHandlerFactory(final @Reference AudioHTTPServer audioHTTPServer,
+ final @Reference NetworkAddressService networkAddressService, final @Reference ApiHandler apiHandler,
+ ComponentContext componentContext) {
+ super.activate(componentContext);
+ this.audioHTTPServer = audioHTTPServer;
+ this.apiHandler = apiHandler;
+
+ ipAddress = networkAddressService.getPrimaryIpv4HostAddress();
+
+ ValidatorFactory validatorFactory = Validation.byDefaultProvider().providerResolver(new OsgiServiceDiscoverer())
+ .configure().messageInterpolator(new ParameterMessageInterpolator()).buildValidatorFactory();
+ validator = validatorFactory.getValidator();
+ }
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ // Validate bean
+ ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+ if (thingTypeUID.equals(BRIDGE_TYPE_API)) {
+ return new FreeboxOsHandler((Bridge) thing, new FreeboxOsSession(apiHandler, validator));
+ } else if (thingTypeUID.equals(THING_TYPE_REVOLUTION)) {
+ return new RevolutionHandler(thing, audioHTTPServer, ipAddress, bundleContext);
+ } else if (thingTypeUID.equals(THING_TYPE_DELTA)) {
+ return new ServerHandler(thing, audioHTTPServer, ipAddress, bundleContext);
+ } else if (thingTypeUID.equals(THING_TYPE_ACTIVE_PLAYER)) {
+ return new ActivePlayerHandler(thing, audioHTTPServer, ipAddress, bundleContext);
+ } else if (thingTypeUID.equals(THING_TYPE_PLAYER)) {
+ return new PlayerHandler(thing, audioHTTPServer, ipAddress, bundleContext);
+ } else if (thingTypeUID.equals(THING_TYPE_HOST)) {
+ return new HostHandler(thing);
+ } else if (thingTypeUID.equals(THING_TYPE_WIFI_HOST)) {
+ return new WifiHostHandler(thing);
+ } else if (thingTypeUID.equals(THING_TYPE_LANDLINE)) {
+ return new LandlineHandler(thing);
+ } else if (thingTypeUID.equals(THING_TYPE_VM)) {
+ return new VmHandler(thing);
+ } else if (thingTypeUID.equals(THING_TYPE_REPEATER)) {
+ return new RepeaterHandler(thing);
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/OsgiServiceDiscoverer.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/OsgiServiceDiscoverer.java
new file mode 100644
index 0000000000000..4df2a8b3c6c54
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/OsgiServiceDiscoverer.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2021 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;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.validation.ValidationProviderResolver;
+import javax.validation.spi.ValidationProvider;
+
+import org.hibernate.validator.HibernateValidator;
+
+/**
+ * The {@link OsgiServiceDiscoverer} provides Hibernate Validator
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+class OsgiServiceDiscoverer implements ValidationProviderResolver {
+
+ @Override
+ public List> getValidationProviders() {
+ return Collections.> singletonList(new HibernateValidator());
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/ActivePlayerActions.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/ActivePlayerActions.java
new file mode 100644
index 0000000000000..768788285ae4f
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/ActivePlayerActions.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2010-2021 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.action;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.handler.ActivePlayerHandler;
+import org.openhab.binding.freeboxos.internal.handler.PlayerHandler;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {ActivePlayerActions} class is responsible to call corresponding
+ * actions on Freebox Player with API
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@ThingActionsScope(name = "freeboxos")
+@NonNullByDefault
+public class ActivePlayerActions extends PlayerActions {
+ private final static Logger logger = LoggerFactory.getLogger(ActivePlayerActions.class);
+
+ @RuleAction(label = "reboot freebox player", description = "Reboots the Freebox Player")
+ public void reboot() {
+ logger.debug("Player reboot called");
+ PlayerHandler playerHandler = this.handler;
+ if (playerHandler instanceof ActivePlayerHandler) {
+ playerHandler.reboot();
+ } else {
+ logger.warn("Freebox Player Action service ThingHandler is null!");
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/HostActions.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/HostActions.java
new file mode 100644
index 0000000000000..ecd03e59ec1a2
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/HostActions.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2010-2021 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.action;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.handler.HostHandler;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {HostActions} class is responsible to call corresponding
+ * actions on a given lan host
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@ThingActionsScope(name = "freeboxos")
+@NonNullByDefault
+public class HostActions implements ThingActions {
+ private final Logger logger = LoggerFactory.getLogger(HostActions.class);
+ private @Nullable HostHandler handler;
+
+ @Override
+ public void setThingHandler(@Nullable ThingHandler handler) {
+ if (handler instanceof HostHandler) {
+ this.handler = (HostHandler) handler;
+ }
+ }
+
+ @Override
+ public @Nullable ThingHandler getThingHandler() {
+ return this.handler;
+ }
+
+ @RuleAction(label = "wol host", description = "Awakes a lan host")
+ public void wol() {
+ logger.debug("Host WOL called");
+ HostHandler hostHandler = this.handler;
+ if (hostHandler != null) {
+ hostHandler.wol();
+ } else {
+ logger.warn("LanHost Action service ThingHandler is null!");
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/PlayerActions.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/PlayerActions.java
new file mode 100644
index 0000000000000..b126434f8c17d
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/PlayerActions.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2010-2021 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.action;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.handler.PlayerHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {PlayerActions} class is responsible to call corresponding
+ * actions on Freebox Player
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@ThingActionsScope(name = "freeboxos")
+@NonNullByDefault
+public class PlayerActions implements ThingActions {
+ private final static Logger logger = LoggerFactory.getLogger(PlayerActions.class);
+ protected @Nullable PlayerHandler handler;
+
+ @Override
+ public void setThingHandler(@Nullable ThingHandler handler) {
+ if (handler instanceof PlayerHandler) {
+ this.handler = (PlayerHandler) handler;
+ }
+ }
+
+ @Override
+ public @Nullable ThingHandler getThingHandler() {
+ return this.handler;
+ }
+
+ @RuleAction(label = "send a key to player", description = "Sends a given key to the player")
+ public void sendKey(@ActionInput(name = "key") String key) {
+ logger.debug("Sending key {} to player", key);
+ PlayerHandler playerHandler = this.handler;
+ if (playerHandler != null) {
+ playerHandler.sendKey(key, false, 1);
+ } else {
+ logger.warn("Freebox Player Action service ThingHandler is null!");
+ }
+ }
+
+ @RuleAction(label = "send a long key to player", description = "Sends a given key to the player and keep it pressed")
+ public void sendLongKey(@ActionInput(name = "key") String key) {
+ logger.debug("Sending long press key {} to player", key);
+ PlayerHandler playerHandler = this.handler;
+ if (playerHandler != null) {
+ playerHandler.sendKey(key, true, 1);
+ } else {
+ logger.warn("Freebox Player Action service ThingHandler is null!");
+ }
+ }
+
+ @RuleAction(label = "send multiple keys to player", description = "Sends multiple keys to the player, comma separated")
+ public void sendMultipleKeys(@ActionInput(name = "keys") String keys) {
+ logger.debug("Sending keys {} to player", keys);
+ PlayerHandler playerHandler = this.handler;
+ if (playerHandler != null) {
+ playerHandler.sendMultipleKeys(keys);
+ } else {
+ logger.warn("Freebox Player Action service ThingHandler is null!");
+ }
+ }
+
+ @RuleAction(label = "send repeating key to player", description = "Sends a given key multiple times to the player")
+ public void sendKeyRepeat(@ActionInput(name = "key") String key, @ActionInput(name = "count") int count) {
+ logger.debug("Sending key {} to player {} times", key, count);
+ PlayerHandler playerHandler = this.handler;
+ if (playerHandler != null) {
+ playerHandler.sendKey(key, false, count);
+ } else {
+ logger.warn("Freebox Player Action service ThingHandler is null!");
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/ServerActions.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/ServerActions.java
new file mode 100644
index 0000000000000..adbb19abd0cb0
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/action/ServerActions.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2010-2021 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.action;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.handler.ServerHandler;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {ServerActions} class is responsible to call corresponding
+ * actions on Freebox Server
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@ThingActionsScope(name = "freeboxos")
+@NonNullByDefault
+public class ServerActions implements ThingActions {
+ private final static Logger logger = LoggerFactory.getLogger(ServerActions.class);
+ private @Nullable ServerHandler handler;
+
+ @Override
+ public void setThingHandler(@Nullable ThingHandler handler) {
+ if (handler instanceof ServerHandler) {
+ this.handler = (ServerHandler) handler;
+ }
+ }
+
+ @Override
+ public @Nullable ThingHandler getThingHandler() {
+ return this.handler;
+ }
+
+ @RuleAction(label = "reboot freebox server", description = "Reboots the Freebox Server")
+ public void reboot() {
+ logger.debug("Server reboot called");
+ ServerHandler serverHandler = this.handler;
+ if (serverHandler != null) {
+ serverHandler.reboot();
+ } else {
+ logger.warn("Freebox Action service ThingHandler is 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
new file mode 100644
index 0000000000000..df8fc50cb12bf
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/ApiHandler.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2010-2021 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.api;
+
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * The {@link ApiHandler} is responsible for sending requests toward
+ * a given url and transform the answer in appropriate dto.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+@Component(service = ApiHandler.class)
+public class ApiHandler {
+ private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(8);
+ private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
+ private static final String AUTH_HEADER = "X-Fbx-App-Auth";
+ private static final String CONTENT_TYPE = "application/json; charset=" + DEFAULT_CHARSET.name();
+
+ private final Logger logger = LoggerFactory.getLogger(ApiHandler.class);
+ private final HttpClient httpClient;
+ private final Gson gson;
+
+ @Activate
+ public ApiHandler(final @Reference HttpClientFactory httpClientFactory,
+ final @Reference TimeZoneProvider timeZoneProvider) {
+ this.httpClient = httpClientFactory.getCommonHttpClient();
+ this.gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+ .registerTypeAdapter(ZonedDateTime.class,
+ (JsonDeserializer) (json, type, jsonDeserializationContext) -> {
+ long timestamp = json.getAsJsonPrimitive().getAsLong();
+ Instant i = Instant.ofEpochSecond(timestamp);
+ return ZonedDateTime.ofInstant(i, timeZoneProvider.getTimeZone());
+ })
+ .create();
+ }
+
+ public synchronized T executeUri(URI uri, HttpMethod method, Type classOfT, @Nullable String sessionToken,
+ @Nullable Object payload) throws FreeboxException {
+ logger.debug("executeUrl {} - {} ", method, uri);
+
+ Request request = httpClient.newRequest(uri).method(method).header(HttpHeader.CONTENT_TYPE, CONTENT_TYPE);
+
+ if (sessionToken != null) {
+ request.header(AUTH_HEADER, sessionToken);
+ }
+
+ if (payload != null) {
+ request.content(new StringContentProvider(gson.toJson(payload), DEFAULT_CHARSET), null);
+ }
+
+ try {
+ ContentResponse serviceResponse = request.timeout(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS).send();
+ String response = new String(serviceResponse.getContent(), DEFAULT_CHARSET);
+
+ logger.trace("executeUrl {} - {} returned {}", method, uri, response);
+
+ return gson.fromJson(response, classOfT);
+ } catch (InterruptedException | TimeoutException | ExecutionException | JsonSyntaxException e) {
+ throw new FreeboxException(e, "Exception while calling " + request.getURI());
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/FreeboxException.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/FreeboxException.java
new file mode 100644
index 0000000000000..9cccae5d004f1
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/FreeboxException.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2010-2021 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.api;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Exception for errors when using the Freebox API
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class FreeboxException extends Exception {
+ private static final long serialVersionUID = 9197365222439228186L;
+
+ private @Nullable Response> response;
+
+ public FreeboxException(String msg) {
+ this(msg, null, null);
+ }
+
+ public FreeboxException(Throwable cause, @Nullable String msg) {
+ this(msg, cause, null);
+ }
+
+ public FreeboxException(@Nullable String msg, @Nullable Throwable cause, @Nullable Response> response) {
+ super(msg, cause);
+ this.response = response;
+ }
+
+ public @Nullable Response> getResponse() {
+ return response;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/FreeboxTlsCertificateProvider.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/FreeboxTlsCertificateProvider.java
new file mode 100644
index 0000000000000..ca79fd87e0a52
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/FreeboxTlsCertificateProvider.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2010-2021 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.api;
+
+import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.DEFAULT_FREEBOX_NAME;
+
+import java.net.URL;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.io.net.http.TlsCertificateProvider;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * Provides a TrustManager for the Freebox SSL certificate
+ *
+ * @author Laurent Garnier - Initial Contribution
+ */
+@Component
+@NonNullByDefault
+public class FreeboxTlsCertificateProvider implements TlsCertificateProvider {
+
+ @Override
+ public String getHostName() {
+ return DEFAULT_FREEBOX_NAME;
+ }
+
+ @Override
+ public URL getCertificate() {
+ URL resource = Thread.currentThread().getContextClassLoader().getResource("freeboxECCRootCA.crt");
+ if (resource != null) {
+ return resource;
+ }
+ throw new IllegalStateException("Certifcate resource not found or not accessible");
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/Response.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/Response.java
new file mode 100644
index 0000000000000..0a9ff8b08c9af
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/Response.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2010-2021 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.api;
+
+import javax.validation.Valid;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.login.Session.Permission;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Defines an API result that returns a single object
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class Response {
+ public static enum ErrorCode {
+ NONE,
+ @SerializedName("auth_required")
+ AUTHORIZATION_REQUIRED,
+ @SerializedName("internal_error")
+ INTERNAL_ERROR,
+ @SerializedName("invalid_token")
+ INVALID_TOKEN;
+ }
+
+ private boolean success;
+ private ErrorCode errorCode = ErrorCode.NONE;
+ private String msg = "";
+
+ private @Nullable Permission missingRight;
+
+ @Valid
+ private @NonNullByDefault({}) T result;
+
+ public T getResult() {
+ return result;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public @Nullable Permission getMissingRight() {
+ return missingRight;
+ }
+
+ public ErrorCode getErrorCode() {
+ return errorCode;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public static Response> of(ErrorCode code) {
+ Response> response = new Response();
+ response.success = false;
+ response.errorCode = code;
+ return response;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaActionData.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaActionData.java
new file mode 100644
index 0000000000000..e0b9a33452abe
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaActionData.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2010-2021 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.api.airmedia;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link AirMediaReceiverRequest} is the Java class used to map the "AirMediaReceiverRequest"
+ * structure used by the sending request to an AirMedia receiver API
+ * https://dev.freebox.fr/sdk/os/airmedia/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class AirMediaActionData {
+ public static enum MediaAction {
+ UNKNOWN,
+ @SerializedName("start")
+ START,
+ @SerializedName("stop")
+ STOP;
+ }
+
+ public static enum MediaType {
+ UNKNOWN,
+ @SerializedName("video")
+ VIDEO,
+ @SerializedName("photo")
+ PHOTO,
+ @SerializedName("audio")
+ AUDIO,
+ @SerializedName("screen")
+ SCREEN;
+ }
+
+ @SuppressWarnings("unused")
+ private final MediaAction action;
+ @SuppressWarnings("unused")
+ private final MediaType mediaType;
+ @SuppressWarnings("unused")
+ private final String password;
+ @SuppressWarnings("unused")
+ private final int position;
+ @SuppressWarnings("unused")
+ private @Nullable String media;
+
+ AirMediaActionData(String password, MediaAction action, MediaType type) {
+ this.password = password;
+ this.action = action;
+ this.mediaType = type;
+ this.position = 0;
+ }
+
+ AirMediaActionData(String password, MediaAction action, MediaType type, String url) {
+ this(password, action, type);
+ this.media = url;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaConfig.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaConfig.java
new file mode 100644
index 0000000000000..2227e4007bade
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaConfig.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2010-2021 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.api.airmedia;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableConfig;
+
+/**
+ * The {@link AirMediaConfig} is the Java class used to map the "AirMediaConfig"
+ * structure used by the AirMedia configuration API
+ * https://dev.freebox.fr/sdk/os/airmedia/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class AirMediaConfig implements ActivableConfig {
+ public static class AirMediaConfigResponse extends Response {
+ }
+
+ private String password;
+ protected boolean enabled;
+
+ private AirMediaConfig(boolean enabled, String password) {
+ this.enabled = enabled;
+ this.password = password;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaManager.java
new file mode 100644
index 0000000000000..5cc23033029e9
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaManager.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2021 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.api.airmedia;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.airmedia.AirMediaConfig.AirMediaConfigResponse;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableRest;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+
+/**
+ * The {@link AirMediaManager} is the Java class used to handle api requests
+ * related to air media
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class AirMediaManager extends ActivableRest {
+ public static final String AIR_MEDIA_PATH = "airmedia";
+
+ public AirMediaManager(FreeboxOsSession session) {
+ super(session, AirMediaConfigResponse.class, AIR_MEDIA_PATH, CONFIG_SUB_PATH);
+ session.addManager(MediaReceiverManager.class, new MediaReceiverManager(session, getUriBuilder()));
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaReceiver.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaReceiver.java
new file mode 100644
index 0000000000000..37ae5a53ca07d
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/AirMediaReceiver.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2010-2021 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.api.airmedia;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.airmedia.AirMediaActionData.MediaType;
+import org.openhab.binding.freeboxos.internal.api.rest.FbxDevice;
+
+/**
+ * The {@link AirMediaReceiver} is the Java class used to map the "AirMediaReceiver"
+ * structure used by the available AirMedia receivers API
+ * https://dev.freebox.fr/sdk/os/airmedia/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class AirMediaReceiver extends FbxDevice {
+ public static class AirMediaReceiversResponse extends Response> {
+ }
+
+ public static class AirMediaReceiverResponse extends Response {
+ }
+
+ private boolean passwordProtected;
+ private Map capabilities = Map.of();
+
+ public boolean isPasswordProtected() {
+ return passwordProtected;
+ }
+
+ public Map getCapabilities() {
+ return capabilities;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/MediaReceiverManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/MediaReceiverManager.java
new file mode 100644
index 0000000000000..33d2226e9b43d
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/airmedia/MediaReceiverManager.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2010-2021 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.api.airmedia;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.airmedia.AirMediaActionData.MediaAction;
+import org.openhab.binding.freeboxos.internal.api.airmedia.AirMediaActionData.MediaType;
+import org.openhab.binding.freeboxos.internal.api.airmedia.AirMediaReceiver.AirMediaReceiverResponse;
+import org.openhab.binding.freeboxos.internal.api.airmedia.AirMediaReceiver.AirMediaReceiversResponse;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.rest.ListableRest;
+
+/**
+ * The {@link MediaReceiverManager} is the Java class used to handle api requests
+ * related to air media
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class MediaReceiverManager
+ extends ListableRest {
+ private static final String RECEIVERS_SUB_PATH = "receivers";
+
+ public MediaReceiverManager(FreeboxOsSession session, UriBuilder uriBuilder) {
+ super(session, AirMediaReceiverResponse.class, AirMediaReceiversResponse.class, uriBuilder, RECEIVERS_SUB_PATH);
+ }
+
+ public void sendToReceiver(String receiver, String password, MediaAction action, MediaType type)
+ throws FreeboxException {
+ sendToReceiver(receiver, new AirMediaActionData(password, action, type));
+ }
+
+ public void sendToReceiver(String receiver, String password, MediaAction action, MediaType type, String url)
+ throws FreeboxException {
+ sendToReceiver(receiver, new AirMediaActionData(password, action, type, url));
+ }
+
+ private void sendToReceiver(String receiver, AirMediaActionData payload) throws FreeboxException {
+ post(payload, receiver);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/call/CallEntry.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/call/CallEntry.java
new file mode 100644
index 0000000000000..ee62ffde3da9f
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/call/CallEntry.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2010-2021 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.api.call;
+
+import java.time.ZonedDateTime;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link CallEntry} is the Java class used to map the "CallEntry"
+ * structure used by the call API
+ * https://dev.freebox.fr/sdk/os/call/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class CallEntry {
+ public class CallEntriesResponse extends Response> {
+ }
+
+ public static enum CallType {
+ UNKNOWN,
+ @SerializedName("accepted")
+ ACCEPTED,
+ @SerializedName("missed")
+ MISSED,
+ @SerializedName("outgoing")
+ OUTGOING,
+ INCOMING;
+ }
+
+ private CallType type = CallType.UNKNOWN;
+ private @Nullable ZonedDateTime datetime; // Call creation timestamp.
+ private @Nullable String number;
+ private @Nullable String name;
+ private int duration; // Call duration in seconds.
+
+ public CallType getType() {
+ if (type == CallType.MISSED && duration == 0) {
+ return CallType.INCOMING;
+ }
+ return type;
+ }
+
+ public @Nullable ZonedDateTime getDatetime() {
+ return datetime;
+ }
+
+ public @Nullable String getPhoneNumber() {
+ return number;
+ }
+
+ public @Nullable String getName() {
+ return name;
+ }
+
+ public int getDuration() {
+ return duration;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/call/CallManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/call/CallManager.java
new file mode 100644
index 0000000000000..947d67868c9a9
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/call/CallManager.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2010-2021 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.api.call;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.call.CallEntry.CallEntriesResponse;
+import org.openhab.binding.freeboxos.internal.api.login.Session.Permission;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.rest.RestManager;
+
+/**
+ * The {@link CallManager} is the Java class used to handle api requests
+ * related to calls
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class CallManager extends RestManager {
+ public final static String CALL_PATH = "call";
+ public final static String LOG_SUB_PATH = "log/";
+
+ public CallManager(FreeboxOsSession session) throws FreeboxException {
+ super(session, Permission.CALLS, CALL_PATH);
+ }
+
+ public List getCallEntries() throws FreeboxException {
+ return getList(CallEntriesResponse.class, LOG_SUB_PATH);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/connection/ConnectionManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/connection/ConnectionManager.java
new file mode 100644
index 0000000000000..dcdbcf3d0c42d
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/connection/ConnectionManager.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2021 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.api.connection;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.connection.ConnectionStatus.ConnectionStatusResponse;
+import org.openhab.binding.freeboxos.internal.api.rest.ConfigurableRest;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+
+/**
+ * The {@link ConnectionManager} is the Java class used to handle api requests
+ * related to connection
+ * https://dev.freebox.fr/sdk/os/system/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class ConnectionManager extends ConfigurableRest {
+ private static final String CONNECTION_PATH = "connection";
+
+ public ConnectionManager(FreeboxOsSession session) {
+ super(session, ConnectionStatusResponse.class, CONNECTION_PATH, null);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/connection/ConnectionStatus.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/connection/ConnectionStatus.java
new file mode 100644
index 0000000000000..e762255e81ade
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/connection/ConnectionStatus.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2010-2021 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.api.connection;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link ConnectionStatus} is the Java class used to map the "ConnectionStatus"
+ * structure used by the connection API
+ * https://dev.freebox.fr/sdk/os/connection/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class ConnectionStatus {
+ public static class ConnectionStatusResponse extends Response {
+ }
+
+ public static enum State {
+ @SerializedName("going_up")
+ GOING_UP,
+ @SerializedName("up")
+ UP,
+ @SerializedName("going_down")
+ GOING_DOWN,
+ @SerializedName("down")
+ DOWN;
+ }
+
+ private @NonNullByDefault({}) State state;
+ private @NonNullByDefault({}) String ipv4;
+ private long rateUp; // current upload rate in byte/s
+ private long rateDown; // current download rate in byte/s
+ private long bandwidthUp; // available upload bandwidth in bit/s
+ private long bandwidthDown; // available download bandwidth in bit/s
+ private long bytesUp; // total uploaded bytes since last connection
+ private long bytesDown; // total downloaded bytes since last connection
+
+ public State getState() {
+ return state;
+ }
+
+ public String getIpv4() {
+ return ipv4;
+ }
+
+ public long getRateUp() {
+ return rateUp;
+ }
+
+ public long getRateDown() {
+ return rateDown;
+ }
+
+ public long getBandwidthUp() {
+ return bandwidthUp;
+ }
+
+ public long getBandwidthDown() {
+ return bandwidthDown;
+ }
+
+ public long getBytesUp() {
+ return bytesUp;
+ }
+
+ public long getBytesDown() {
+ return bytesDown;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/ftp/FtpConfig.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/ftp/FtpConfig.java
new file mode 100644
index 0000000000000..697a0475ad2ea
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/ftp/FtpConfig.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2010-2021 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.api.ftp;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableConfig;
+
+/**
+ * The {@link FtpConfig} is the Java class used to map the "FtpConfig"
+ * structure used by the FTP configuration API
+ * https://dev.freebox.fr/sdk/os/ftp/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class FtpConfig implements ActivableConfig {
+ public static class FtpConfigResponse extends Response {
+ }
+
+ protected boolean enabled;
+ protected boolean allowAnonymous;
+ protected boolean allowAnonymousWrite;
+ protected String password = "";
+ protected boolean allowRemoteAccess;
+ protected boolean weakPassword;
+ protected int portCtrl;
+ protected int portData;
+ protected String remoteDomain = "";
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/ftp/FtpManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/ftp/FtpManager.java
new file mode 100644
index 0000000000000..1f0ac3dae9b5f
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/ftp/FtpManager.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2021 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.api.ftp;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.ftp.FtpConfig.FtpConfigResponse;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableRest;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+
+/**
+ * The {@link FtpManager} is the Java class used to handle api requests
+ * related to ftp
+ * https://dev.freebox.fr/sdk/os/system/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class FtpManager extends ActivableRest {
+ private static final String FTP_PATH = "ftp";
+
+ public FtpManager(FreeboxOsSession session) {
+ super(session, FtpConfigResponse.class, FTP_PATH, CONFIG_SUB_PATH);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/ConnectivityData.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/ConnectivityData.java
new file mode 100644
index 0000000000000..dfef340a66a7e
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/ConnectivityData.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lan;
+
+import java.time.ZonedDateTime;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link ConnectivityData} is a common interface for lanhosts
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public interface ConnectivityData {
+ public boolean isReachable();
+
+ public @Nullable ZonedDateTime getLastSeen();
+
+ public @Nullable String getIpv4();
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/L2Ident.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/L2Ident.java
new file mode 100644
index 0000000000000..eaa38f045ef47
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/L2Ident.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lan;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link L2Ident} is the Java class used to map the "LanHostL2Ident"
+ * structure used by the Lan Hosts Browser API
+ * https://dev.freebox.fr/sdk/os/lan/#lan-browser
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+class L2Ident {
+ private static final String L2_TYPE_MAC_ADDRESS = "mac_address";
+
+ private @NonNullByDefault({}) String id;
+ private @Nullable String type;
+
+ public @Nullable String getIfMac() {
+ return L2_TYPE_MAC_ADDRESS.equalsIgnoreCase(type) ? id.toLowerCase() : null;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/L3Connectivity.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/L3Connectivity.java
new file mode 100644
index 0000000000000..75a66816a1b15
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/L3Connectivity.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lan;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link L3Connectivity} is the Java class used to map the "LanHostL3Connectivity"
+ * structure used by the Lan Hosts Browser API
+ * https://dev.freebox.fr/sdk/os/lan/#lan-browser
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class L3Connectivity {
+ private static enum L3Af {
+ UNKNOWN,
+ @SerializedName("ipv4")
+ IPV4,
+ @SerializedName("ipv6")
+ IPV6;
+ }
+
+ private @NonNullByDefault({}) String addr;
+ private @Nullable L3Af af;
+ private boolean active;
+
+ private L3Af getAf() {
+ L3Af localAf = af;
+ return localAf != null ? localAf : L3Af.UNKNOWN;
+ }
+
+ public @Nullable String getIpv4() {
+ return getAf() == L3Af.IPV4 ? addr : null;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanAccessPoint.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanAccessPoint.java
new file mode 100644
index 0000000000000..3a21c3cd455ab
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanAccessPoint.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lan;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.wifi.WifiInformation;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link LanAccessPoint} is the Java class used to map the "LanHostL3Connectivity"
+ * structure used by the Lan Hosts Browser API
+ * https://dev.freebox.fr/sdk/os/lan/#lan-browser
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class LanAccessPoint {
+ @SerializedName("uid")
+ private @NonNullByDefault({}) String id;
+ private @Nullable String type;
+ private @Nullable WifiInformation wifiInformation;
+
+ public @Nullable String getType() {
+ return type;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public @Nullable String getSsid() {
+ return wifiInformation != null ? wifiInformation.getSsid() : null;
+ }
+
+ public int getSignal() {
+ // Valid RSSI values goes from -120 to 0.
+ return wifiInformation != null ? wifiInformation.getSignal() : 1;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanBrowserManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanBrowserManager.java
new file mode 100644
index 0000000000000..43666f2701ce4
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanBrowserManager.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lan;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.lan.LanHost.LanHostsResponse;
+import org.openhab.binding.freeboxos.internal.api.lan.LanInterface.LanInterfaceResponse;
+import org.openhab.binding.freeboxos.internal.api.lan.LanInterface.LanInterfacesResponse;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.rest.ListableRest;
+
+/**
+ * The {@link LanBrowserManager} is the Java class used to handle api requests
+ * related to lan
+ * https://dev.freebox.fr/sdk/os/system/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class LanBrowserManager extends ListableRest {
+ private static final String WOL_SUB_PATH = "wol";
+ private static final String DEFAULT_INTF = "pub";
+ private static final String INTERFACES_SUB_PATH = "interfaces";
+ private static final String BROWSER_SUB_PATH = "browser";
+
+ public LanBrowserManager(FreeboxOsSession session, UriBuilder uriBuilder) {
+ super(session, LanInterfaceResponse.class, LanInterfacesResponse.class, uriBuilder, BROWSER_SUB_PATH);
+ listSubPath = INTERFACES_SUB_PATH;
+ }
+
+ private List getInterfaceHosts(String lanInterface) throws FreeboxException {
+ return getList(LanHostsResponse.class, lanInterface);
+ }
+
+ private synchronized List getHosts() throws FreeboxException {
+ List hosts = new ArrayList<>();
+
+ for (LanInterface intf : getDevices()) {
+ String name = intf.getName();
+ if (name != null) {
+ List intfHosts = getInterfaceHosts(name);
+ hosts.addAll(intfHosts);
+ }
+ }
+ return hosts;
+ }
+
+ public Map getHostsMap() throws FreeboxException {
+ Map result = new HashMap<>();
+ getHosts().stream().forEach(host -> {
+ String mac = host.getMac();
+ if (mac != null) {
+ result.put(mac, host);
+ }
+ });
+ return result;
+ }
+
+ public Optional getHost(String mac) throws FreeboxException {
+ return Optional.ofNullable(getHostsMap().get(mac));
+ }
+
+ public void wakeOnLan(String host) throws FreeboxException {
+ WakeOnLineData wol = new WakeOnLineData(host);
+ // TODO: default interface should be dynamically resolved
+ post(wol, WOL_SUB_PATH, DEFAULT_INTF, host);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanConfig.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanConfig.java
new file mode 100644
index 0000000000000..406635c4f0b9d
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanConfig.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lan;
+
+import java.time.ZonedDateTime;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link LanConfig} is the Java class used to map the "LanConfig"
+ * structure used by the LAN configuration API
+ * https://dev.freebox.fr/sdk/os/lan/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class LanConfig implements ConnectivityData {
+ public static class LanConfigResponse extends Response {
+ }
+
+ public static enum NetworkMode {
+ UNKNOWN,
+ @SerializedName("router")
+ ROUTER,
+ @SerializedName("bridge")
+ BRIDGE;
+ }
+
+ private @NonNullByDefault({}) String name;
+ private @Nullable String ip;
+ private @Nullable NetworkMode mode;
+
+ public NetworkMode getMode() {
+ NetworkMode localMode = mode;
+ return localMode != null ? localMode : NetworkMode.UNKNOWN;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isReachable() {
+ return true;
+ }
+
+ @Override
+ public @Nullable ZonedDateTime getLastSeen() {
+ return ZonedDateTime.now();
+ }
+
+ @Override
+ public @Nullable String getIpv4() {
+ return ip;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanHost.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanHost.java
new file mode 100644
index 0000000000000..43738ce1e38fe
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanHost.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lan;
+
+import java.time.ZonedDateTime;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+/**
+ * The {@link LanHost} is the Java class used to map the "LanHost"
+ * structure used by the Lan Hosts Browser API
+ * https://dev.freebox.fr/sdk/os/lan/#lan-browser
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class LanHost implements ConnectivityData {
+ public static class LanHostsResponse extends Response> {
+ }
+
+ private @Nullable String primaryName;
+ private @Nullable L2Ident l2ident;
+ private @Nullable String vendorName;
+ private @Nullable List l3connectivities;
+ private @Nullable LanAccessPoint accessPoint;
+ private boolean reachable;
+ private @Nullable ZonedDateTime lastTimeReachable;
+ private @Nullable ZonedDateTime lastActivity;
+
+ public @Nullable String getMac() {
+ return l2ident != null ? l2ident.getIfMac() : null;
+ }
+
+ @Override
+ public @Nullable String getIpv4() {
+ List connectivities = l3connectivities;
+ return connectivities != null
+ ? connectivities.stream().filter(c -> c.isActive() && c.getIpv4() != null).map(c -> c.getIpv4())
+ .findFirst().orElse(null)
+ : null;
+ }
+
+ public Optional getPrimaryName() {
+ return Optional.ofNullable(primaryName);
+ }
+
+ public Optional getVendorName() {
+ return Optional.ofNullable(vendorName);
+ }
+
+ @Override
+ public boolean isReachable() {
+ return reachable;
+ }
+
+ @Override
+ public @Nullable ZonedDateTime getLastSeen() {
+ ZonedDateTime localLastActivity = lastActivity;
+ if (lastTimeReachable == null && localLastActivity == null) {
+ return null;
+ }
+ if (lastTimeReachable == null) {
+ return lastActivity;
+ }
+ if (localLastActivity == null) {
+ return lastTimeReachable;
+ } else {
+ return localLastActivity.isAfter(lastTimeReachable) ? lastActivity : lastTimeReachable;
+ }
+ }
+
+ public @Nullable LanAccessPoint getAccessPoint() {
+ return accessPoint;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanInterface.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanInterface.java
new file mode 100644
index 0000000000000..003e808a2f52f
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanInterface.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lan;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.rest.FbxDevice;
+
+/**
+ * The {@link LanInterface} is the Java class used to map the
+ * structure used by the Lan Interface Browser API
+ * https://dev.freebox.fr/sdk/os/lan/#lan-browser
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+class LanInterface extends FbxDevice {
+ public static class LanInterfacesResponse extends Response> {
+ }
+
+ public static class LanInterfaceResponse extends Response {
+ }
+
+ // private @NonNullByDefault({}) String name;
+
+ // public String getName() {
+ // return name;
+ // }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanManager.java
new file mode 100644
index 0000000000000..0c04c1a367090
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/LanManager.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lan;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.lan.LanConfig.LanConfigResponse;
+import org.openhab.binding.freeboxos.internal.api.lan.LanConfig.NetworkMode;
+import org.openhab.binding.freeboxos.internal.api.rest.ConfigurableRest;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+
+/**
+ * The {@link LanManager} is the Java class used to handle api requests
+ * related to lan
+ * https://dev.freebox.fr/sdk/os/system/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class LanManager extends ConfigurableRest {
+ public static final String LAN_SUB_PATH = "lan";
+ private final NetworkMode networkMode;
+
+ public LanManager(FreeboxOsSession session) throws FreeboxException {
+ super(session, LanConfigResponse.class, LAN_SUB_PATH, CONFIG_SUB_PATH);
+ this.networkMode = getConfig().getMode();
+ session.addManager(LanBrowserManager.class, new LanBrowserManager(session, getUriBuilder()));
+ }
+
+ public NetworkMode getNetworkMode() throws FreeboxException {
+ return networkMode;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/NameSource.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/NameSource.java
new file mode 100644
index 0000000000000..40c5c22d65e43
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/NameSource.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lan;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link NameSource} holds various classes of name sources
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public enum NameSource {
+ @SerializedName("dhcp")
+ DHCP,
+ @SerializedName("netbios")
+ NETBIOS,
+ @SerializedName("mdns")
+ MDNS,
+ @SerializedName("upnp")
+ UPNP;
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/WakeOnLineData.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/WakeOnLineData.java
new file mode 100644
index 0000000000000..fe80ff645b3d8
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lan/WakeOnLineData.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lan;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link WakeOnLineData} is the Java class used to send a
+ * WOL order to the given host
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+class WakeOnLineData {
+ protected final String mac;
+ protected final String password;
+
+ WakeOnLineData(String mac) {
+ this(mac, "");
+ }
+
+ private WakeOnLineData(String mac, String password) {
+ this.mac = mac;
+ this.password = password;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lcd/LcdConfig.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lcd/LcdConfig.java
new file mode 100644
index 0000000000000..8c625722b14bd
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lcd/LcdConfig.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lcd;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+/**
+ * The {@link LcdConfig} is the Java class used to map the "LcdConfig"
+ * structure used by the LCD configuration API
+ * https://dev.freebox.fr/sdk/os/lcd/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class LcdConfig {
+ // Response classes
+ public static class LcdConfigResponse extends Response {
+ }
+
+ private int brightness;
+ private boolean orientationForced;
+ private int orientation;
+
+ public int getBrightness() {
+ return brightness;
+ }
+
+ public void setBrightness(int brightness) {
+ this.brightness = Math.max(0, Math.min(100, brightness));
+ }
+
+ public boolean isOrientationForced() {
+ return orientationForced;
+ }
+
+ public void setOrientationForced(boolean orientationForced) {
+ this.orientationForced = orientationForced;
+ }
+
+ public int getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(int orientation) {
+ this.orientation = Math.max(0, Math.min(360, orientation));
+ this.orientationForced = true;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lcd/LcdManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lcd/LcdManager.java
new file mode 100644
index 0000000000000..aee0190823387
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/lcd/LcdManager.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2021 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.api.lcd;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.lcd.LcdConfig.LcdConfigResponse;
+import org.openhab.binding.freeboxos.internal.api.rest.ConfigurableRest;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+
+/**
+ * The {@link LcdManager} is the Java class used to handle api requests
+ * related to lcd screen of the server
+ * https://dev.freebox.fr/sdk/os/system/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class LcdManager extends ConfigurableRest {
+ private static final String LCD_SUB_PATH = "lcd";
+
+ public LcdManager(FreeboxOsSession session) {
+ super(session, LcdConfigResponse.class, LCD_SUB_PATH, CONFIG_SUB_PATH);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/Authorize.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/Authorize.java
new file mode 100644
index 0000000000000..d26f8ee172be4
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/Authorize.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2010-2021 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.api.login;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotEmpty;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+/**
+ * The {@link Authorize} is the Java class used to map the
+ * structure used by the response of the request authorization API
+ * https://dev.freebox.fr/sdk/os/login/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+class Authorize {
+ public static class AuthorizeResponse extends Response {
+ }
+
+ @NotEmpty(message = "No app token provided")
+ private @NonNullByDefault({}) String appToken;
+
+ @Min(value = 1, message = "Missing or invalid trackId")
+ private int trackId;
+
+ public String getAppToken() {
+ return appToken;
+ }
+
+ public int getTrackId() {
+ return trackId;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/AuthorizeData.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/AuthorizeData.java
new file mode 100644
index 0000000000000..d12631976d468
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/AuthorizeData.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2021 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.api.login;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.osgi.framework.Bundle;
+
+/**
+ * The {@link AuthorizeData} holds and handle data needed to
+ * be sent to API in order to get authorization
+ * https://dev.freebox.fr/sdk/os/login/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+class AuthorizeData {
+ protected final String appId;
+ protected final String appName;
+ protected final String appVersion;
+ protected final String deviceName;
+
+ AuthorizeData(String appId, Bundle bundle) {
+ this.appId = appId;
+ this.appName = bundle.getHeaders().get("Bundle-Name");
+ this.appVersion = bundle.getVersion().toString();
+ this.deviceName = bundle.getHeaders().get("Bundle-Vendor");
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/Challenge.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/Challenge.java
new file mode 100644
index 0000000000000..bf7f5d792efd5
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/Challenge.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2010-2021 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.api.login;
+
+import javax.validation.constraints.NotEmpty;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link Challenge} holds and handle data needed to
+ * be sent to API in order to get authorization
+ * https://dev.freebox.fr/sdk/os/login/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class Challenge {
+ public static class ChallengeResponse extends Response {
+ }
+
+ public static enum Status {
+ @SerializedName("unknown")
+ UNKNOWN, // the app_token is invalid or has been revoked
+ @SerializedName("pending")
+ PENDING, // the user has not confirmed the autorization request yet
+ @SerializedName("timeout")
+ TIMEOUT, // the user did not confirmed the authorization within the given time
+ @SerializedName("granted")
+ GRANTED, // the app_token is valid and can be used to open a session
+ @SerializedName("denied")
+ DENIED;// the user denied the authorization request
+ }
+
+ private Status status = Status.UNKNOWN;
+
+ @NotEmpty(message = "Challenge value should not be null")
+ private @NonNullByDefault({}) String challenge;
+ private boolean loggedIn;
+
+ public boolean isLoggedIn() {
+ return loggedIn;
+ }
+
+ public String getChallenge() {
+ return challenge;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/LoginManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/LoginManager.java
new file mode 100644
index 0000000000000..6c890fde17b61
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/LoginManager.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2010-2021 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.api.login;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.login.Authorize.AuthorizeResponse;
+import org.openhab.binding.freeboxos.internal.api.login.Challenge.ChallengeResponse;
+import org.openhab.binding.freeboxos.internal.api.login.Challenge.Status;
+import org.openhab.binding.freeboxos.internal.api.login.Session.SessionResponse;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.rest.RestManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * The {@link LoginManager} is the Java class used to handle api requests
+ * related to session handling and login
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class LoginManager extends RestManager {
+ private static final String LOGIN_PATH = "login";
+ private static final String AUTHORIZE_PATH = "authorize";
+ private static final String LOGOUT_PATH = "logout";
+ private static final String SESSION_PATH = "session";
+ private static final Bundle BUNDLE = FrameworkUtil.getBundle(LoginManager.class);
+ private static final String APP_ID = BUNDLE.getSymbolicName();
+
+ public LoginManager(FreeboxOsSession session) {
+ super(session, LOGIN_PATH);
+ }
+
+ public Session openSession(String appToken) throws FreeboxException {
+ String challenge = get(ChallengeResponse.class).getChallenge();
+ OpenSessionData payload = new OpenSessionData(APP_ID, appToken, challenge);
+ return post(SessionResponse.class, payload, SESSION_PATH);
+ }
+
+ public void closeSession() throws FreeboxException {
+ post(LOGOUT_PATH);
+ }
+
+ private Status trackAuthorize(int trackId) throws FreeboxException {
+ return get(ChallengeResponse.class, AUTHORIZE_PATH, Integer.toString(trackId)).getStatus();
+ }
+
+ public String grant() throws FreeboxException {
+ Authorize authorize = post(AuthorizeResponse.class, new AuthorizeData(APP_ID, BUNDLE), AUTHORIZE_PATH);
+ Status track = Status.PENDING;
+ try {
+ while (track == Status.PENDING) {
+ Thread.sleep(2000);
+ track = trackAuthorize(authorize.getTrackId());
+ }
+ if (track == Status.GRANTED) {
+ return authorize.getAppToken();
+ }
+ throw new FreeboxException("Unable to grant session");
+ } catch (InterruptedException e) {
+ throw new FreeboxException(e, "Granting process interrupted");
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/OpenSessionData.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/OpenSessionData.java
new file mode 100644
index 0000000000000..e31efde56b314
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/OpenSessionData.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2010-2021 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.api.login;
+
+import static javax.xml.bind.DatatypeConverter.printHexBinary;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.Response.ErrorCode;
+
+/**
+ * The {@link OpenSessionData} holds and handle data needed to
+ * be sent to API in order to open a new session
+ * https://dev.freebox.fr/sdk/os/login/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class OpenSessionData {
+ private static final String ALGORITHM = "HmacSHA1";
+ protected final String appId;
+ protected final String password;
+
+ public OpenSessionData(String appId, String appToken, String challenge) throws FreeboxException {
+ this.appId = appId;
+ try {
+ Mac mac = Mac.getInstance(ALGORITHM);
+
+ // Initialize mac with the signing key
+ mac.init(new SecretKeySpec(appToken.getBytes(), mac.getAlgorithm()));
+
+ // Compute the hmac on input data bytes
+ byte[] rawHmac = mac.doFinal(challenge.getBytes());
+
+ // Convert raw bytes to Hex
+ this.password = printHexBinary(rawHmac).toLowerCase();
+ } catch (IllegalArgumentException | NoSuchAlgorithmException | InvalidKeyException e) {
+ throw new FreeboxException("Error encoding session password", e, Response.of(ErrorCode.INVALID_TOKEN));
+ }
+ }
+
+ public String getPassword() {
+ return password;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/Session.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/Session.java
new file mode 100644
index 0000000000000..dcfde74d2c4b3
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/login/Session.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2010-2021 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.api.login;
+
+import java.util.Map;
+
+import javax.validation.constraints.NotEmpty;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link Session} is the Java class used to map the
+ * structure used by the response of the open session API
+ * https://dev.freebox.fr/sdk/os/login/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class Session {
+ public static class SessionResponse extends Response {
+ }
+
+ public static enum Permission {
+ @SerializedName("parental")
+ PARENTAL,
+ @SerializedName("contacts")
+ CONTACTS,
+ @SerializedName("explorer")
+ EXPLORER,
+ @SerializedName("tv")
+ TV,
+ @SerializedName("wdo")
+ WDO,
+ @SerializedName("downloader")
+ DOWNLOADER,
+ @SerializedName("profile")
+ PROFILE,
+ @SerializedName("camera")
+ CAMERA,
+ @SerializedName("settings")
+ SETTINGS,
+ @SerializedName("calls")
+ CALLS,
+ @SerializedName("home")
+ HOME,
+ @SerializedName("pvr")
+ PVR,
+ @SerializedName("vm")
+ VM,
+ @SerializedName("player")
+ PLAYER;
+ }
+
+ private Map permissions = Map.of();
+ @NotEmpty(message = "Session token can not be null or Empty")
+ private @NonNullByDefault({}) String sessionToken;
+
+ public String getSessionToken() {
+ return sessionToken;
+ }
+
+ public boolean hasPermission(Permission checked) {
+ return Boolean.TRUE.equals(permissions.get(checked));
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/netshare/NetShareManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/netshare/NetShareManager.java
new file mode 100644
index 0000000000000..b5d96b7a0d2f4
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/netshare/NetShareManager.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2010-2021 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.api.netshare;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.netshare.SambaConfig.SambaConfigResponse;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableRest;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.rest.RestManager;
+
+/**
+ * The {@link NetShareManager} is the Java class used to handle api requests
+ * related to network shares
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class NetShareManager extends RestManager {
+ private static final String NETSHARE_SUB_PATH = "netshare";
+
+ public class SambaManager extends ActivableRest {
+ private static final String SAMBA_SUB_PATH = "samba";
+
+ public SambaManager(FreeboxOsSession session, UriBuilder uriBuilder) {
+ super(session, SambaConfigResponse.class, uriBuilder, SAMBA_SUB_PATH, null);
+ }
+ }
+
+ public NetShareManager(FreeboxOsSession session) {
+ super(session, NETSHARE_SUB_PATH);
+ session.addManager(SambaManager.class, new SambaManager(session, getUriBuilder()));
+
+ // TODO : on pourra ajouter la gestion des partages Mac OS
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/netshare/SambaConfig.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/netshare/SambaConfig.java
new file mode 100644
index 0000000000000..e3094a0b20b05
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/netshare/SambaConfig.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2010-2021 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.api.netshare;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableConfig;
+
+/**
+ * The {@link SambaConfig} is the Java class used to map answer
+ * returned by the Samba configuration API
+ * https://dev.freebox.fr/sdk/os/network_share/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class SambaConfig implements ActivableConfig {
+ // Response classes
+ public static class SambaConfigResponse extends Response {
+ }
+
+ private boolean fileShareEnabled;
+ private boolean printShareEnabled;
+ protected boolean logonEnabled;
+ protected @Nullable String logonUser;
+ protected @Nullable String logonPassword;
+ protected @Nullable String workgroup;
+
+ @Override
+ public boolean isEnabled() {
+ return fileShareEnabled;
+ }
+
+ @Override
+ public void setEnabled(boolean fileShareEnabled) {
+ this.fileShareEnabled = fileShareEnabled;
+ }
+
+ public boolean isPrintShareEnabled() {
+ return printShareEnabled;
+ }
+
+ public void setPrintShareEnabled(boolean printShareEnabled) {
+ this.printShareEnabled = printShareEnabled;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/phone/PhoneConfig.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/phone/PhoneConfig.java
new file mode 100644
index 0000000000000..b34eef63ad670
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/phone/PhoneConfig.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2010-2021 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.api.phone;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableConfig;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class PhoneConfig implements ActivableConfig {
+ public class PhoneConfigResponse extends Response {
+ }
+
+ protected @NonNullByDefault({}) String network;
+ @SerializedName("dect_eco_mode")
+ protected boolean dectEcoMode;
+ @SerializedName("dect_pin")
+ protected @NonNullByDefault({}) String dectPin;
+ @SerializedName("dect_ring_pattern")
+ protected int dectRingPattern;
+ @SerializedName("dect_registration")
+ protected boolean dectRegistration;
+ @SerializedName("dect_nemo_mode")
+ protected boolean dectNemoMode;
+ @SerializedName("dect_enabled")
+ protected boolean dectEnabled;
+ @SerializedName("dect_ring_on_off")
+ protected boolean dectRingOnOff;
+
+ public boolean isDectRingOnOff() {
+ return dectRingOnOff;
+ }
+
+ public void setDectRingOnOff(boolean dectRingOnOff) {
+ this.dectRingOnOff = dectRingOnOff;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return dectEnabled;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ this.dectEnabled = enabled;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/phone/PhoneManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/phone/PhoneManager.java
new file mode 100644
index 0000000000000..3e99e5d81af3b
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/phone/PhoneManager.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2010-2021 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.api.phone;
+
+import java.util.List;
+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.login.Session.Permission;
+import org.openhab.binding.freeboxos.internal.api.phone.PhoneConfig.PhoneConfigResponse;
+import org.openhab.binding.freeboxos.internal.api.phone.PhoneStatus.PhoneStatusResponse;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableRest;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+
+/**
+ * The {@link PhoneManager} is the Java class used to handle api requests
+ * related to phone and calls
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class PhoneManager extends ActivableRest {
+ private static final String PHONE_SUB_PATH = "phone";
+
+ public PhoneManager(FreeboxOsSession session) throws FreeboxException {
+ super(session, Permission.CALLS, PhoneConfigResponse.class, PHONE_SUB_PATH, CONFIG_SUB_PATH);
+ }
+
+ public List getPhoneStatuses() throws FreeboxException {
+ return getList(PhoneStatusResponse.class, "");
+ }
+
+ public Optional getStatus(int id) throws FreeboxException {
+ List statuses = getPhoneStatuses();
+ return statuses.stream().filter(status -> status.getId() == id).findFirst();
+ }
+
+ public void ring(boolean startIt) throws FreeboxException {
+ post(String.format("fxs_ring_%s", (startIt ? "start" : "stop")));
+ }
+
+ public void alternateRing(boolean status) throws FreeboxException {
+ PhoneConfig config = getConfig();
+ config.setDectRingOnOff(status);
+ put(PhoneConfigResponse.class, config, CONFIG_SUB_PATH);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/phone/PhoneStatus.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/phone/PhoneStatus.java
new file mode 100644
index 0000000000000..4f8afe239286f
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/phone/PhoneStatus.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2010-2021 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.api.phone;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link PhoneStatus} is the Java class used to map the
+ * structure used by the phone API
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class PhoneStatus {
+ public static class PhoneStatusResponse extends Response> {
+ }
+
+ public enum PhoneType {
+ UNKNOWN,
+ @SerializedName("fxs")
+ LAND_LINE,
+ @SerializedName("dect")
+ DECT;
+ }
+
+ private int id;
+ private boolean isRinging;
+ private boolean onHook;
+ private PhoneType type = PhoneType.UNKNOWN;
+
+ public boolean isRinging() {
+ return isRinging;
+ }
+
+ public boolean isOnHook() {
+ return onHook;
+ }
+
+ public PhoneType getType() {
+ return type;
+ }
+
+ public long getId() {
+ return id;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/player/Player.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/player/Player.java
new file mode 100644
index 0000000000000..795780cc2e7ff
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/player/Player.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2010-2021 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.api.player;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.rest.FbxDevice;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class Player extends FbxDevice {
+ public static class PlayersResponse extends Response> {
+ }
+
+ public static class PlayerResponse extends Response {
+ }
+
+ private boolean apiAvailable;
+ private boolean reachable;
+
+ public boolean isApiAvailable() {
+ return apiAvailable;
+ }
+
+ public boolean isReachable() {
+ return reachable;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/player/PlayerManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/player/PlayerManager.java
new file mode 100644
index 0000000000000..4ea8f770f96c7
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/player/PlayerManager.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2010-2021 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.api.player;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.login.Session.Permission;
+import org.openhab.binding.freeboxos.internal.api.player.Player.PlayerResponse;
+import org.openhab.binding.freeboxos.internal.api.player.Player.PlayersResponse;
+import org.openhab.binding.freeboxos.internal.api.player.PlayerStatus.PlayerStatusResponse;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.rest.ListableRest;
+import org.openhab.binding.freeboxos.internal.api.system.DeviceConfig;
+import org.openhab.binding.freeboxos.internal.api.system.DeviceConfig.DeviceConfigurationResponse;
+
+/**
+ * The {@link PlayerManager} is the Java class used to handle api requests
+ * related to player
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class PlayerManager extends ListableRest {
+ private static final String STATUS_SUB_PATH = "status";
+ private static final String PLAYER_SUB_PATH = "player";
+
+ private final Map subPaths = new HashMap<>();
+
+ public PlayerManager(FreeboxOsSession session) throws FreeboxException {
+ super(session, Permission.PLAYER, PlayerResponse.class, PlayersResponse.class, PLAYER_SUB_PATH);
+ getDevices().forEach(player -> {
+ subPaths.put(player.getId(), player.baseUrl());
+ });
+ }
+
+ public PlayerStatus getPlayerStatus(int id) throws FreeboxException {
+ return get(PlayerStatusResponse.class, subPaths.get(id), STATUS_SUB_PATH);
+ }
+
+ public DeviceConfig getConfig(int id) throws FreeboxException {
+ return get(DeviceConfigurationResponse.class, subPaths.get(id), SYSTEM_SUB_PATH);
+ }
+
+ public void reboot(int id) throws FreeboxException {
+ post(subPaths.get(id), SYSTEM_SUB_PATH, REBOOT_SUB_PATH);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/player/PlayerStatus.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/player/PlayerStatus.java
new file mode 100644
index 0000000000000..bb301e932ca24
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/player/PlayerStatus.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2010-2021 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.api.player;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link PlayerStatus} is the Java class used to map the "ConnectionStatus"
+ * structure used by the connection API
+ * https://dev.freebox.fr/sdk/os/connection/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class PlayerStatus {
+ public static class PlayerStatusResponse extends Response {
+ }
+
+ public static enum PowerState {
+ UNKNOWN,
+ @SerializedName("standby")
+ STANDBY,
+ @SerializedName("running")
+ RUNNING;
+ }
+
+ private @Nullable PowerState powerState;
+
+ public PowerState getPowerState() {
+ PowerState power = powerState;
+ return power != null ? power : PowerState.UNKNOWN;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/repeater/Repeater.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/repeater/Repeater.java
new file mode 100644
index 0000000000000..0162ea6014f8a
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/repeater/Repeater.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2010-2021 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.api.repeater;
+
+import java.time.ZonedDateTime;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.rest.FbxDevice;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class Repeater extends FbxDevice {
+ public static class RepeatersResponse extends Response> {
+ }
+
+ public static class RepeaterResponse extends Response {
+ }
+
+ private @Nullable String connection;
+ private @Nullable ZonedDateTime bootTime;
+ private boolean ledActivated;
+ private @NonNullByDefault({}) String sn;
+ private @NonNullByDefault({}) String firmwareVersion;
+
+ public @Nullable ZonedDateTime getBootTime() {
+ return bootTime;
+ }
+
+ public @Nullable String getConnection() {
+ return connection;
+ }
+
+ public boolean getLedActivated() {
+ return ledActivated;
+ }
+
+ public String getSerial() {
+ return sn;
+ }
+
+ public String getFirmwareVersion() {
+ return firmwareVersion;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/repeater/RepeaterManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/repeater/RepeaterManager.java
new file mode 100644
index 0000000000000..dc78b22ff610b
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/repeater/RepeaterManager.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2010-2021 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.api.repeater;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.lan.LanHost;
+import org.openhab.binding.freeboxos.internal.api.lan.LanHost.LanHostsResponse;
+import org.openhab.binding.freeboxos.internal.api.repeater.Repeater.RepeaterResponse;
+import org.openhab.binding.freeboxos.internal.api.repeater.Repeater.RepeatersResponse;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.rest.ListableRest;
+
+/**
+ * The {@link RepeaterManager} is the Java class used to handle api requests
+ * related to repeater
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class RepeaterManager extends ListableRest {
+ private static final String HOST_SUB_PATH = "host";
+ private final static String REPEATER_SUB_PATH = "repeater";
+
+ public RepeaterManager(FreeboxOsSession session) throws FreeboxException {
+ super(session, RepeaterResponse.class, RepeatersResponse.class, REPEATER_SUB_PATH);
+ }
+
+ public List getRepeaterHosts(int id) throws FreeboxException {
+ return getList(LanHostsResponse.class, Integer.toString(id), HOST_SUB_PATH);
+ }
+
+ private synchronized List getHosts() throws FreeboxException {
+ List hosts = new ArrayList<>();
+ for (Repeater rep : getDevices()) {
+ List repHosts = getRepeaterHosts(rep.getId());
+ hosts.addAll(repHosts);
+ }
+ return hosts;
+ }
+
+ public Map getHostsMap() throws FreeboxException {
+ Map result = new HashMap<>();
+ getHosts().stream().forEach(host -> {
+ String mac = host.getMac();
+ if (mac != null) {
+ result.put(mac, host);
+ }
+ });
+ return result;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ActivableConfig.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ActivableConfig.java
new file mode 100644
index 0000000000000..70aac4496dfe3
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ActivableConfig.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2021 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.api.rest;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link ActivableConfig} is the interface for config messages
+ * that hols activation / deactivation of a service
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public interface ActivableConfig {
+ public boolean isEnabled();
+
+ public void setEnabled(boolean enabled);
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ActivableRest.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ActivableRest.java
new file mode 100644
index 0000000000000..06f18a76b405e
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ActivableRest.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2010-2021 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.api.rest;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.login.Session.Permission;
+
+/**
+ * The {@link ActivableRest} is the Java class used to handle portions of the
+ * Api that accept to get and set configuration
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class ActivableRest> extends ConfigurableRest {
+
+ public ActivableRest(FreeboxOsSession session, Class classOfResponse, String path, String configPath) {
+ super(session, classOfResponse, path, configPath);
+ }
+
+ public ActivableRest(FreeboxOsSession session, Class classOfResponse, UriBuilder parentUri, String path,
+ @Nullable String configPath) {
+ super(session, classOfResponse, parentUri, path, configPath);
+ }
+
+ public ActivableRest(FreeboxOsSession session, Permission required, Class classOfResponse, String path,
+ @Nullable String configPath) throws FreeboxException {
+ super(session, classOfResponse, required, path, configPath);
+ }
+
+ public boolean getStatus() throws FreeboxException {
+ return getConfig().isEnabled();
+ }
+
+ public boolean setStatus(boolean enabled) throws FreeboxException {
+ T config = getConfig();
+ config.setEnabled(enabled);
+ return setConfig(config).isEnabled();
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ApiVersion.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ApiVersion.java
new file mode 100644
index 0000000000000..533aa553eea54
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ApiVersion.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2010-2021 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.api.rest;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link ApiVersion} is the Java class used to map the api_version answer
+ * http://mafreebox.freebox.fr/api_version
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class ApiVersion {
+ private String apiBaseUrl = "/api/";
+ @SerializedName(value = "api_version", alternate = { "api_ver" })
+ private @NonNullByDefault({}) String apiVersion;
+
+ /**
+ * @return a string like eg : '/api/v8'
+ */
+ public String baseUrl() {
+ return String.format("%sv%s", apiBaseUrl, apiVersion.split("\\.")[0]);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ConfigurableRest.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ConfigurableRest.java
new file mode 100644
index 0000000000000..c91124dfc5544
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ConfigurableRest.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2010-2021 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.api.rest;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.login.Session.Permission;
+
+/**
+ * The {@link ConfigurableRest} is the Java class used to handle portions of the
+ * Api that accept to get and set configuration
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class ConfigurableRest> extends RestManager {
+ private final Class responseClass;
+ private final @Nullable String configPath;
+
+ public ConfigurableRest(FreeboxOsSession session, Class classOfResponse, String path,
+ @Nullable String configPath) {
+ super(session, path);
+ this.responseClass = classOfResponse;
+ this.configPath = configPath;
+ }
+
+ public ConfigurableRest(FreeboxOsSession session, Class classOfResponse, UriBuilder parentUri, String path,
+ @Nullable String configPath) {
+ super(session, parentUri, path);
+ this.responseClass = classOfResponse;
+ this.configPath = configPath;
+ }
+
+ public ConfigurableRest(FreeboxOsSession session, Class classOfResponse, Permission required, String path,
+ @Nullable String configPath) throws FreeboxException {
+ super(session, required, path);
+ this.responseClass = classOfResponse;
+ this.configPath = configPath;
+ }
+
+ public T getConfig() throws FreeboxException {
+ String path = configPath;
+ return path != null ? get(responseClass, path) : get(responseClass);
+ }
+
+ public T setConfig(T config) throws FreeboxException {
+ String path = configPath;
+ return path != null ? put(responseClass, config, path) : put(responseClass, config);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/FbxDevice.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/FbxDevice.java
new file mode 100644
index 0000000000000..3f2bdb7e9734a
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/FbxDevice.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2010-2021 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.api.rest;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link FbxDevice} is the Java class used to describe most of
+ * Free equipments via API
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class FbxDevice extends ApiVersion {
+
+ private int id;
+
+ @SerializedName(value = "mac", alternate = { "main_mac" })
+ private @NonNullByDefault({}) String mac;
+
+ @SerializedName(value = "device_name", alternate = { "name" })
+ private @Nullable String name;
+
+ @SerializedName(value = "device_model", alternate = { "model" })
+ private String model = "";
+
+ public int getId() {
+ return id;
+ }
+
+ public String getMac() {
+ return mac.toLowerCase();
+ }
+
+ public @Nullable String getName() {
+ return name;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ /**
+ * @return a string like eg : '17/api/v8'
+ */
+ @Override
+ public String baseUrl() {
+ return String.format("%d%s/", id, super.baseUrl());
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/FreeboxOsSession.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/FreeboxOsSession.java
new file mode 100644
index 0000000000000..98339cb34b318
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/FreeboxOsSession.java
@@ -0,0 +1,173 @@
+/**
+ * Copyright (c) 2010-2021 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.api.rest;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+import javax.ws.rs.core.UriBuilder;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.http.HttpMethod;
+import org.openhab.binding.freeboxos.internal.api.ApiHandler;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.Response.ErrorCode;
+import org.openhab.binding.freeboxos.internal.api.lan.LanManager;
+import org.openhab.binding.freeboxos.internal.api.login.LoginManager;
+import org.openhab.binding.freeboxos.internal.api.login.Session;
+import org.openhab.binding.freeboxos.internal.api.login.Session.Permission;
+import org.openhab.binding.freeboxos.internal.api.netshare.NetShareManager;
+import org.openhab.binding.freeboxos.internal.api.wifi.WifiManager;
+import org.openhab.binding.freeboxos.internal.config.FreeboxOsConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link FreeboxOsSession} is responsible for sending requests toward
+ * a given url and transform the answer in appropriate dto.
+ *
+ * @author Gaël L'Hopital - Initial contribution
+ */
+@NonNullByDefault
+public class FreeboxOsSession {
+ private final Logger logger = LoggerFactory.getLogger(FreeboxOsSession.class);
+ private final Map, RestManager> restManagers = new HashMap<>();
+
+ private final ApiHandler apiHandler;
+ private final Validator validator;
+
+ private @NonNullByDefault({}) UriBuilder uriBuilder;
+ private @Nullable String appToken;
+ private @Nullable Session session;
+
+ public FreeboxOsSession(ApiHandler apiHandler, Validator validator) {
+ this.apiHandler = apiHandler;
+ this.validator = validator;
+ }
+
+ /**
+ * @param configuration
+ * @return the app token used to open the session (can have changed if newly granted)
+ * @throws FreeboxException
+ */
+ public String initialize(FreeboxOsConfiguration configuration) throws FreeboxException {
+ UriBuilder uriBuilder = UriBuilder.fromPath("/").scheme(configuration.getScheme()).port(configuration.getPort())
+ .host(configuration.apiDomain);
+ ApiVersion version = apiHandler.executeUri(uriBuilder.clone().path("api_version").build(), HttpMethod.GET,
+ ApiVersion.class, null, configuration);
+ this.appToken = configuration.appToken;
+ this.uriBuilder = uriBuilder.path(version.baseUrl());
+ return initiateConnection();
+ }
+
+ private String initiateConnection() throws FreeboxException {
+ try {
+ String localToken = appToken;
+ if (localToken != null) {
+ session = getManager(LoginManager.class).openSession(localToken);
+ getManager(NetShareManager.class);
+ getManager(LanManager.class);
+ getManager(WifiManager.class);
+ return localToken;
+ }
+ throw new FreeboxException(null, null, Response.of(ErrorCode.INVALID_TOKEN));
+ } catch (FreeboxException e) {
+ Response> response = e.getResponse();
+ if (response != null && response.getErrorCode() == ErrorCode.INVALID_TOKEN) {
+ appToken = getManager(LoginManager.class).grant();
+ return initiateConnection();
+ }
+ throw e;
+ }
+ }
+
+ public void closeSession() {
+ if (session != null) {
+ try {
+ getManager(LoginManager.class).closeSession();
+ } catch (FreeboxException e) {
+ logger.info("Error closing session : {}", e.getMessage());
+ }
+ session = null;
+ }
+ appToken = null;
+ restManagers.clear();
+ }
+
+ private @Nullable String sessionToken() {
+ return session != null ? session.getSessionToken() : null;
+ }
+
+ private > F execute(URI uri, HttpMethod method, Class classOfT, boolean retryAuth,
+ int retryCount, @Nullable Object aPayload) throws FreeboxException {
+ T response = apiHandler.executeUri(uri, method, classOfT, sessionToken(), aPayload);
+ if (response.getErrorCode() == ErrorCode.INTERNAL_ERROR && retryCount > 0) {
+ return execute(uri, method, classOfT, false, retryCount - 1, aPayload);
+ } else if (retryAuth && response.getErrorCode() == ErrorCode.AUTHORIZATION_REQUIRED) {
+ initiateConnection();
+ return execute(uri, method, classOfT, false, retryCount, aPayload);
+ }
+ if (!response.isSuccess()) {
+ throw new FreeboxException("Api request failed : " + response.getMsg());
+ } else {
+ Set>> constraintViolations = validator.validate(response);
+ if (!constraintViolations.isEmpty()) {
+ ConstraintViolation> violation = constraintViolations.iterator().next();
+ throw new FreeboxException(violation.getMessage() + " on request : " + uri.toString(), null, response);
+ }
+ }
+ return response.getResult();
+ }
+
+ > F execute(URI uri, HttpMethod method, Class classOfT, @Nullable Object aPayload)
+ throws FreeboxException {
+ boolean retryAuth = sessionToken() != null;
+ return execute(uri, method, classOfT, retryAuth, 3, aPayload);
+ }
+
+ @SuppressWarnings("unchecked")
+ public synchronized T getManager(Class classOfT) throws FreeboxException {
+ RestManager manager = restManagers.get(classOfT);
+ if (manager == null) {
+ try {
+ Constructor managerConstructor = classOfT.getConstructor(FreeboxOsSession.class);
+ manager = managerConstructor.newInstance(this);
+ restManagers.put(classOfT, manager);
+ } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException | NoSuchMethodException | SecurityException e) {
+ throw new FreeboxException(e, "Unable to call RestManager constructor for " + classOfT.getName());
+ }
+ }
+ return (T) manager;
+ }
+
+ public void addManager(Class classOfT, RestManager manager) {
+ restManagers.put(classOfT, manager);
+ }
+
+ boolean hasPermission(Permission required) {
+ return session != null && session.hasPermission(required);
+ }
+
+ public UriBuilder getUriBuilder() {
+ return uriBuilder.clone();
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ListableRest.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ListableRest.java
new file mode 100644
index 0000000000000..cb58eb28e9ddf
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/ListableRest.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2010-2021 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.api.rest;
+
+import java.util.List;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.login.Session.Permission;
+
+/**
+ * The {@link ListableRest} is the Java class used to handle portions of the
+ * Api that accept to get and set configuration
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class ListableRest, Y extends Response>> extends RestManager {
+ private final Class listRespClass;
+ private final Class devRespClass;
+ @Nullable
+ protected String listSubPath = null;
+
+ public ListableRest(FreeboxOsSession session, Class devRespClass, Class listRespClass,
+ String... pathElements) {
+ super(session, pathElements);
+ this.listRespClass = listRespClass;
+ this.devRespClass = devRespClass;
+ }
+
+ public ListableRest(FreeboxOsSession session, Class devRespClass, Class listRespClass, UriBuilder parentUri,
+ String... pathElements) {
+ super(session, parentUri, pathElements);
+ this.listRespClass = listRespClass;
+ this.devRespClass = devRespClass;
+ }
+
+ public ListableRest(FreeboxOsSession session, Permission required, Class devRespClass, Class listRespClass,
+ String... pathElements) throws FreeboxException {
+ super(session, required, pathElements);
+ this.listRespClass = listRespClass;
+ this.devRespClass = devRespClass;
+ }
+
+ public List getDevices() throws FreeboxException {
+ if (listSubPath == null) {
+ return get(listRespClass);
+ } else {
+ return getList(listRespClass, listSubPath);
+ }
+ }
+
+ public T getDevice(int deviceId) throws FreeboxException {
+ return get(devRespClass, deviceSubPath(deviceId));
+ }
+
+ protected String deviceSubPath(int deviceId) {
+ return String.format("%d", deviceId);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/RestManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/RestManager.java
new file mode 100644
index 0000000000000..f24fe435a16b6
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/RestManager.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2010-2021 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.api.rest;
+
+import static org.eclipse.jetty.http.HttpMethod.*;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.login.Session.Permission;
+
+/**
+ * Base class for all various rest managers
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class RestManager {
+ protected static final String CONFIG_SUB_PATH = "config";
+ protected static final String REBOOT_SUB_PATH = "reboot";
+ protected static final String SYSTEM_SUB_PATH = "system";
+
+ private final UriBuilder uriBuilder;
+ protected FreeboxOsSession session;
+
+ public RestManager(FreeboxOsSession session, String... pathElements) {
+ this.uriBuilder = assemblePath(session.getUriBuilder(), pathElements);
+ this.session = session;
+ }
+
+ public RestManager(FreeboxOsSession session, UriBuilder parentUri, String... pathElements) {
+ this.uriBuilder = assemblePath(parentUri, pathElements);
+ this.session = session;
+ }
+
+ public RestManager(FreeboxOsSession session, Permission required, String... pathElements) throws FreeboxException {
+ this(session, pathElements);
+ if (!session.hasPermission(required)) {
+ throw new FreeboxException("Permission missing : " + required.toString());
+ }
+ }
+
+ protected UriBuilder getUriBuilder() {
+ return uriBuilder.clone();
+ }
+
+ private UriBuilder assemblePath(UriBuilder uriBuilder, String... pathElements) {
+ for (String path : pathElements) {
+ uriBuilder.path(path);
+ }
+ return uriBuilder;
+ }
+
+ private URI buildUri(String... pathElements) {
+ return assemblePath(getUriBuilder(), pathElements).build();
+ }
+
+ @SuppressWarnings("null")
+ protected >> List getList(Class classOfT, String... pathElements)
+ throws FreeboxException {
+ // GetList may return null object because API does not return anything for empty lists
+ List result = session.execute(buildUri(pathElements), GET, classOfT, null);
+ return result != null ? result : List.of();
+ }
+
+ protected > F get(Class classOfT) throws FreeboxException {
+ return session.execute(getUriBuilder().build(), GET, classOfT, null);
+ }
+
+ protected > F get(Class classOfT, String... pathElements) throws FreeboxException {
+ return session.execute(buildUri(pathElements), GET, classOfT, null);
+ }
+
+ protected void post(Object payload, String... pathElements) throws FreeboxException {
+ session.execute(buildUri(pathElements), POST, GenericResponse.class, payload);
+ }
+
+ protected void post(String... pathElements) throws FreeboxException {
+ session.execute(buildUri(pathElements), POST, GenericResponse.class, null);
+ }
+
+ protected > F post(Class classOfT, Object payload, String... pathElements)
+ throws FreeboxException {
+ return session.execute(buildUri(pathElements), POST, classOfT, payload);
+ }
+
+ protected > F put(Class classOfT, F payload) throws FreeboxException {
+ return session.execute(getUriBuilder().build(), PUT, classOfT, payload);
+ }
+
+ protected > F put(Class classOfT, F payload, String... pathElements)
+ throws FreeboxException {
+ return session.execute(buildUri(pathElements), PUT, classOfT, payload);
+ }
+
+ private class GenericResponse extends Response {
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/DeviceConfig.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/DeviceConfig.java
new file mode 100644
index 0000000000000..fa5c1f91a5df9
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/DeviceConfig.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2010-2021 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.api.system;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+/**
+ * The {@link DeviceConfig} is the Java class used to map minimal common
+ * structure used by the system API in respose to get configuration requests
+ * https://dev.freebox.fr/sdk/os/system/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class DeviceConfig {
+ public static class DeviceConfigurationResponse extends Response {
+ }
+
+ private class ModelInfo {
+ private @NonNullByDefault({}) String prettyName;
+ }
+
+ private @Nullable ModelInfo modelInfo;
+ private long uptimeVal;
+ private @NonNullByDefault({}) String boardName;
+ private @NonNullByDefault({}) String serial;
+ private @NonNullByDefault({}) String firmwareVersion;
+
+ public String getFirmwareVersion() {
+ return firmwareVersion;
+ }
+
+ public String getSerial() {
+ return serial;
+ }
+
+ public long getUptimeVal() {
+ return uptimeVal;
+ }
+
+ public String getBoardName() {
+ return boardName;
+ }
+
+ public Optional getPrettyName() {
+ ModelInfo info = this.modelInfo;
+ return info != null ? Optional.of(info.prettyName) : Optional.empty();
+ }
+
+ protected @Nullable List getSensors() {
+ return null;
+ }
+
+ protected @Nullable List getFans() {
+ return null;
+ }
+
+ public List getAllSensors() {
+ ArrayList result = new ArrayList<>();
+
+ List currentList = getFans();
+ if (currentList != null) {
+ result.addAll(currentList);
+ }
+ currentList = getSensors();
+ if (currentList != null) {
+ result.addAll(currentList);
+ }
+ return result;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/Sensor.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/Sensor.java
new file mode 100644
index 0000000000000..2cea42c177633
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/Sensor.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2010-2021 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.api.system;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link Sensor} is the Java class used to map the fans and sensors part of the "SystemConfig"
+ * structure used by the system API
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class Sensor {
+ private final Logger logger = LoggerFactory.getLogger(Sensor.class);
+
+ public enum SensorKind {
+ FAN("Vitesse"),
+ TEMP("Température"),
+ UNKNOWN("Uknown");
+
+ private String label;
+
+ SensorKind(String label) {
+ this.label = label;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+ }
+
+ private @NonNullByDefault({}) String id;
+ private @NonNullByDefault({}) String name;
+ private int value;
+
+ public String getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public SensorKind getKind() {
+ String[] elements = id.split("_");
+ if (elements.length > 0) {
+ String kind = elements[0].replaceAll("\\d", "").toUpperCase();
+ try {
+ return SensorKind.valueOf(kind);
+ } catch (IllegalArgumentException ignore) {
+ // Will be logged and result UNKNOWN returned
+ }
+ }
+ logger.warn("Unknown sensor retrieved : {}", id);
+ return SensorKind.UNKNOWN;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/SystemConf.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/SystemConf.java
new file mode 100644
index 0000000000000..0e6bb2fd876db
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/SystemConf.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2010-2021 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.api.system;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.Response;
+
+/**
+ * The {@link SystemConf} is the Java class used to map the "SystemConfig"
+ * structure used by the system API
+ * https://dev.freebox.fr/sdk/os/system/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class SystemConf extends DeviceConfig {
+ // Response classes
+ public static class SystemConfigurationResponse extends Response {
+ }
+
+ private @NonNullByDefault({}) String mac;
+ private @Nullable List fans;
+ private @Nullable List sensors;
+
+ public String getMac() {
+ return mac.toLowerCase();
+ }
+
+ @Override
+ protected @Nullable List getSensors() {
+ return sensors;
+ }
+
+ @Override
+ protected @Nullable List getFans() {
+ return fans;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/SystemManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/SystemManager.java
new file mode 100644
index 0000000000000..c7a8593d4fc4c
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/system/SystemManager.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2010-2021 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.api.system;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.rest.ConfigurableRest;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.system.SystemConf.SystemConfigurationResponse;
+
+/**
+ * The {@link SystemManager} is the Java class used to handle api requests
+ * related to system
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class SystemManager extends ConfigurableRest {
+ public SystemManager(FreeboxOsSession session) {
+ super(session, SystemConfigurationResponse.class, SYSTEM_SUB_PATH, null);
+ }
+
+ public void reboot() throws FreeboxException {
+ post(REBOOT_SUB_PATH);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/upnpav/UPnPAVConfig.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/upnpav/UPnPAVConfig.java
new file mode 100644
index 0000000000000..8ae6bb1d9ee1b
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/upnpav/UPnPAVConfig.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2010-2021 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.api.upnpav;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableConfig;
+
+/**
+ * The {@link UPnPAVConfig} is the Java class used to map the "UPnPAVConfig"
+ * structure used by the UPnP AV configuration API
+ * https://dev.freebox.fr/sdk/os/upnpav/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class UPnPAVConfig implements ActivableConfig {
+ public static class UPnPAVConfigResponse extends Response {
+ }
+
+ private boolean enabled;
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/upnpav/UPnPAVManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/upnpav/UPnPAVManager.java
new file mode 100644
index 0000000000000..d0c4ee5829edb
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/upnpav/UPnPAVManager.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2021 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.api.upnpav;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableRest;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.upnpav.UPnPAVConfig.UPnPAVConfigResponse;
+
+/**
+ * The {@link UPnPAVManager} is the Java class used to handle api requests
+ * related to UPnP AV
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class UPnPAVManager extends ActivableRest {
+ private final static String UPNPAV_URL = "upnpav";
+
+ public UPnPAVManager(FreeboxOsSession session) {
+ super(session, UPnPAVConfigResponse.class, UPNPAV_URL, CONFIG_SUB_PATH);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/vm/VirtualMachine.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/vm/VirtualMachine.java
new file mode 100644
index 0000000000000..9e61e0416041c
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/vm/VirtualMachine.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2010-2021 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.api.vm;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.rest.FbxDevice;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link VirtualMachine} is the Java class used to map the "VirtualMachine"
+ * structure used by the Virtual Machine API
+ * https://dev.freebox.fr/sdk/os/lan/#lan-browser
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class VirtualMachine extends FbxDevice {
+ public class VirtualMachineResponse extends Response {
+ }
+
+ public class VirtualMachinesResponse extends Response> {
+ }
+
+ public static enum Status {
+ UNKNOWN,
+ @SerializedName("stopped")
+ STOPPED,
+ @SerializedName("running")
+ RUNNING;
+ }
+
+ private Status status = Status.UNKNOWN;
+
+ public Status getStatus() {
+ return status;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/vm/VmManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/vm/VmManager.java
new file mode 100644
index 0000000000000..9c845386652cb
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/vm/VmManager.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2021 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.api.vm;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.login.Session.Permission;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.rest.ListableRest;
+import org.openhab.binding.freeboxos.internal.api.vm.VirtualMachine.VirtualMachineResponse;
+import org.openhab.binding.freeboxos.internal.api.vm.VirtualMachine.VirtualMachinesResponse;
+
+/**
+ * The {@link VmManager} is the Java class used to handle api requests
+ * related to virtual machines
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class VmManager extends ListableRest {
+ private static final String VM_SUB_PATH = "vm";
+
+ public VmManager(FreeboxOsSession session) throws FreeboxException {
+ super(session, Permission.VM, VirtualMachineResponse.class, VirtualMachinesResponse.class, VM_SUB_PATH);
+ }
+
+ public void power(int vmId, boolean startIt) throws FreeboxException {
+ post(deviceSubPath(vmId), startIt ? "start" : "powerbutton");
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/APManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/APManager.java
new file mode 100644
index 0000000000000..4e2e46b78312e
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/APManager.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2010-2021 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.api.wifi;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.rest.ListableRest;
+import org.openhab.binding.freeboxos.internal.api.wifi.AccessPoint.AccessPointResponse;
+import org.openhab.binding.freeboxos.internal.api.wifi.AccessPoint.AccessPointsResponse;
+import org.openhab.binding.freeboxos.internal.api.wifi.AccessPointHost.AccessPointHostsResponse;
+
+/**
+ * The {@link APManager} is the Java class used to handle api requests
+ * related to access points
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class APManager extends ListableRest {
+ private static final String STATIONS_SUB_PATH = "stations";
+ private static final String AP_SUB_PATH = "ap";
+
+ public APManager(FreeboxOsSession session, UriBuilder uriBuilder) {
+ super(session, AccessPointResponse.class, AccessPointsResponse.class, uriBuilder, AP_SUB_PATH);
+ }
+
+ private @Nullable List getAccessPointHosts(int apId) throws FreeboxException {
+ return getList(AccessPointHostsResponse.class, Integer.toString(apId), STATIONS_SUB_PATH);
+ }
+
+ public Map getHostsMap() throws FreeboxException {
+ Map result = new HashMap<>();
+ getHosts().stream().forEach(host -> {
+ String mac = host.getMac();
+ result.put(mac, host);
+ });
+ return result;
+ }
+
+ private List getHosts() throws FreeboxException {
+ List hosts = new ArrayList<>();
+ for (AccessPoint ap : getDevices()) {
+ List apHosts = getAccessPointHosts(ap.getId());
+ if (apHosts != null) {
+ hosts.addAll(apHosts);
+ }
+ }
+ return hosts;
+ }
+
+ public Optional getHost(String mac) throws FreeboxException {
+ return getHosts().stream().filter(host -> host.getMac().equalsIgnoreCase(mac)).findFirst();
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/AccessPoint.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/AccessPoint.java
new file mode 100644
index 0000000000000..42574a5ed1ac6
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/AccessPoint.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2010-2021 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.api.wifi;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.rest.FbxDevice;
+
+/**
+ * The {@link AccessPoint} is the Java class used to map the "SwitchStatus"
+ * structure used by the response of the switch status API
+ * https://dev.freebox.fr/sdk/os/switch/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class AccessPoint extends FbxDevice {
+ public class AccessPointsResponse extends Response> {
+ }
+
+ public class AccessPointResponse extends Response {
+ }
+
+ // private int id;
+ // private @NonNullByDefault({}) String name;
+
+ // public String getName() {
+ // return name;
+ // }
+
+ // public int getId() {
+ // return id;
+ // }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/AccessPointHost.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/AccessPointHost.java
new file mode 100644
index 0000000000000..9d36f01996842
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/AccessPointHost.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2010-2021 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.api.wifi;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.lan.LanAccessPoint;
+import org.openhab.binding.freeboxos.internal.api.lan.LanHost;
+
+/**
+ * The {@link AccessPointHost} is the Java class used to map the "SwitchStatus"
+ * structure used by the response of the switch status API
+ * https://dev.freebox.fr/sdk/os/switch/#
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class AccessPointHost {
+ public class AccessPointHostsResponse extends Response> {
+ }
+
+ private @NonNullByDefault({}) String bssid;
+ private @Nullable LanHost host;
+ private @NonNullByDefault({}) String mac;
+ private int signal;
+
+ public int getSignal() {
+ return signal;
+ }
+
+ public String getBssid() {
+ return bssid;
+ }
+
+ public String getMac() {
+ return mac.toLowerCase();
+ }
+
+ public @Nullable String getSsid() {
+ if (host != null) {
+ LanAccessPoint accessPoint = host.getAccessPoint();
+ if (accessPoint != null) {
+ return accessPoint.getSsid();
+ }
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/WifiConfig.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/WifiConfig.java
new file mode 100644
index 0000000000000..9f5e74a6a99e7
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/WifiConfig.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2010-2021 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.api.wifi;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.Response;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableConfig;
+
+/**
+ * The {@link WifiConfig} is the Java class used to map the "WifiGlobalConfig"
+ * structure used by the Wifi global configuration API
+ * https://dev.freebox.fr/sdk/os/wifi/#
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class WifiConfig implements ActivableConfig {
+ // Response classes
+ public static class WifiConfigResponse extends Response {
+ }
+
+ private boolean enabled;
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/WifiInformation.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/WifiInformation.java
new file mode 100644
index 0000000000000..2536a70ff89d1
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/WifiInformation.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2021 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.api.wifi;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class WifiInformation {
+ private @NonNullByDefault({}) String ssid;
+ private int signal;
+
+ public String getSsid() {
+ return ssid;
+ }
+
+ public int getSignal() {
+ return signal;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/WifiManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/WifiManager.java
new file mode 100644
index 0000000000000..194aceca5e997
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/wifi/WifiManager.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2021 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.api.wifi;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.rest.ActivableRest;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.wifi.WifiConfig.WifiConfigResponse;
+
+/**
+ * The {@link WifiManager} is the Java class used to handle api requests
+ * related to wifi
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class WifiManager extends ActivableRest {
+ public static final String WIFI_SUB_PATH = "wifi";
+
+ public WifiManager(FreeboxOsSession session) {
+ super(session, WifiConfigResponse.class, WIFI_SUB_PATH, CONFIG_SUB_PATH);
+ session.addManager(APManager.class, new APManager(session, getUriBuilder()));
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/ApiConsumerConfiguration.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/ApiConsumerConfiguration.java
new file mode 100644
index 0000000000000..2c72c7b497937
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/ApiConsumerConfiguration.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2010-2021 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.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class ApiConsumerConfiguration {
+ public int refreshInterval = 30;
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/ClientConfiguration.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/ClientConfiguration.java
new file mode 100644
index 0000000000000..e2dffec685d17
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/ClientConfiguration.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2021 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.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link ClientConfiguration} is responsible for holding
+ * configuration informations for a controllable client of the API
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class ClientConfiguration extends HostConfiguration {
+ public static final String ID = "id";
+
+ public int id = 1;
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/FreeboxOsConfiguration.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/FreeboxOsConfiguration.java
new file mode 100644
index 0000000000000..f77abfd74cb04
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/FreeboxOsConfiguration.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2010-2021 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.config;
+
+import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.DEFAULT_FREEBOX_NAME;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link FreeboxOsConfiguration} is responsible for holding
+ * configuration informations needed to access the Freebox API
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class FreeboxOsConfiguration {
+ public static final String API_DOMAIN = "api_domain";
+ public static final String HTTPS_AVAILABLE = "https_available";
+ public static final String HTTPS_PORT = "https_port";
+ public static final String APP_TOKEN = "appToken";
+
+ private int httpsPort = 15682;
+ private boolean httpsAvailable;
+
+ public String apiDomain = DEFAULT_FREEBOX_NAME;
+ public @Nullable String appToken;
+ public boolean discoverNetDevice;
+
+ public String getScheme() {
+ return httpsAvailable ? "https" : "http";
+ }
+
+ public int getPort() {
+ return httpsAvailable ? httpsPort : 80;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/HostConfiguration.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/HostConfiguration.java
new file mode 100644
index 0000000000000..a528a42db6e31
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/HostConfiguration.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2010-2021 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.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link HostConfiguration} is responsible for holding
+ * configuration informations associated to a Freebox Network Device
+ * thing type
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class HostConfiguration extends ApiConsumerConfiguration {
+ private String macAddress = "";
+
+ public String getMac() {
+ return macAddress.toLowerCase();
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/LandlineConfiguration.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/LandlineConfiguration.java
new file mode 100644
index 0000000000000..8465b095519b8
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/LandlineConfiguration.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2010-2021 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.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link LandlineConfiguration} is responsible for holding
+ * configuration informations associated to a Freebox Phone thing type
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class LandlineConfiguration extends ApiConsumerConfiguration {
+ public int id = 1;
+
+ LandlineConfiguration() {
+ refreshInterval = 2;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/PlayerConfiguration.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/PlayerConfiguration.java
new file mode 100644
index 0000000000000..23f793c8cfc0c
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/PlayerConfiguration.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2021 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.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link PlayerConfiguration} is responsible for holding
+ * configuration informations needed to access/poll the freebox player
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class PlayerConfiguration extends ClientConfiguration {
+ public static final String PORT = "port";
+ public static final String REMOTE_CODE = "remoteCode";
+ public static final String PASSWORD = "password";
+ public static final String CALLBACK_URL = "callBackUrl";
+
+ public int port = 24322;
+ public String password = "";
+ public boolean acceptAllMp3 = true;
+ public String remoteCode = "";
+ public String callBackUrl = "";
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/console/FreeboxOsCommandExtension.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/console/FreeboxOsCommandExtension.java
new file mode 100644
index 0000000000000..5f966f67eb087
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/console/FreeboxOsCommandExtension.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2010-2021 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.console;
+
+import static org.openhab.binding.freeboxos.internal.config.FreeboxOsConfiguration.APP_TOKEN;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants;
+import org.openhab.binding.freeboxos.internal.handler.FreeboxOsHandler;
+import org.openhab.core.io.console.Console;
+import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
+import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingRegistry;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link FreeboxOsCommandExtension} is responsible for handling console commands
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+
+@NonNullByDefault
+@Component(service = ConsoleCommandExtension.class)
+public class FreeboxOsCommandExtension extends AbstractConsoleCommandExtension {
+
+ private final ThingRegistry thingRegistry;
+
+ @Activate
+ public FreeboxOsCommandExtension(final @Reference ThingRegistry thingRegistry) {
+ super(FreeboxOsBindingConstants.BINDING_ID, "Interact with the Freebox OS binding.");
+ this.thingRegistry = thingRegistry;
+ }
+
+ @Override
+ public void execute(String[] args, Console console) {
+ if (args.length == 2) {
+ Thing thing = null;
+ try {
+ ThingUID thingUID = new ThingUID(args[0]);
+ thing = thingRegistry.get(thingUID);
+ } catch (IllegalArgumentException e) {
+ thing = null;
+ }
+ ThingHandler thingHandler = null;
+ FreeboxOsHandler handler = null;
+ if (thing != null) {
+ thingHandler = thing.getHandler();
+ if (thingHandler instanceof FreeboxOsHandler) {
+ handler = (FreeboxOsHandler) thingHandler;
+ }
+ }
+ if (thing == null) {
+ console.println("Bad thing id '" + args[0] + "'");
+ printUsage(console);
+ } else if (thingHandler == null) {
+ console.println("No handler initialized for the thing id '" + args[0] + "'");
+ printUsage(console);
+ } else if (handler == null) {
+ console.println("'" + args[0] + "' is not a freebox bridge id");
+ printUsage(console);
+ } else {
+ switch (args[1]) {
+ case APP_TOKEN:
+ String token = handler.getConfiguration().appToken;
+ console.println("Your application token is "
+ + (token == null || token.isEmpty() ? "undefined" : token));
+ break;
+ default:
+ printUsage(console);
+ break;
+ }
+ }
+ } else {
+ printUsage(console);
+ }
+ }
+
+ @Override
+ public List getUsages() {
+ return Arrays.asList(buildCommandUsage(String.format(" %s show the application token", APP_TOKEN)));
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/discovery/ApiDiscoveryParticipant.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/discovery/ApiDiscoveryParticipant.java
new file mode 100644
index 0000000000000..0fd55359807f3
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/discovery/ApiDiscoveryParticipant.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2010-2021 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.discovery;
+
+import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
+import static org.openhab.binding.freeboxos.internal.config.FreeboxOsConfiguration.*;
+
+import java.util.Set;
+
+import javax.jmdns.ServiceInfo;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link ApiDiscoveryParticipant} is responsible for discovering
+ * the various servers flavors of bridges thing using mDNS discovery service
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@Component // (service = MDNSDiscoveryParticipant.class, configurationPid = "mdnsdiscovery.freeboxos")
+@NonNullByDefault
+public class ApiDiscoveryParticipant implements MDNSDiscoveryParticipant {
+ private final Logger logger = LoggerFactory.getLogger(ApiDiscoveryParticipant.class);
+
+ @Override
+ public Set getSupportedThingTypeUIDs() {
+ return BRIDGE_TYPE_UID;
+ }
+
+ @Override
+ public String getServiceType() {
+ return "_fbx-api._tcp.local.";
+ }
+
+ @Override
+ public @Nullable DiscoveryResult createResult(ServiceInfo service) {
+ logger.debug("createResult ServiceInfo: {}", service);
+ ThingUID thingUID = getThingUID(service);
+ return thingUID != null
+ ? DiscoveryResultBuilder.create(thingUID).withLabel("Bridge Freebox OS")
+ .withRepresentationProperty(API_DOMAIN)
+ .withProperty(HTTPS_AVAILABLE, "1".equals(service.getPropertyString(HTTPS_AVAILABLE)))
+ .withProperty(HTTPS_PORT, service.getPropertyString(HTTPS_PORT))
+ .withProperty(API_DOMAIN, service.getPropertyString(API_DOMAIN)).build()
+ : null;
+ }
+
+ @Override
+ public @Nullable ThingUID getThingUID(ServiceInfo service) {
+ String apiDomain = service.getPropertyString(API_DOMAIN);
+ if (apiDomain != null) {
+ String[] elements = apiDomain.split("\\.");
+ if (elements.length > 0) {
+ return new ThingUID(BRIDGE_TYPE_API, elements[0]);
+ }
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/discovery/FreeboxOsDiscoveryService.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/discovery/FreeboxOsDiscoveryService.java
new file mode 100644
index 0000000000000..952e22c4881d8
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/discovery/FreeboxOsDiscoveryService.java
@@ -0,0 +1,226 @@
+/**
+ * Copyright (c) 2010-2021 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.discovery;
+
+import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.lan.LanBrowserManager;
+import org.openhab.binding.freeboxos.internal.api.lan.LanHost;
+import org.openhab.binding.freeboxos.internal.api.phone.PhoneManager;
+import org.openhab.binding.freeboxos.internal.api.phone.PhoneStatus;
+import org.openhab.binding.freeboxos.internal.api.player.Player;
+import org.openhab.binding.freeboxos.internal.api.player.PlayerManager;
+import org.openhab.binding.freeboxos.internal.api.repeater.Repeater;
+import org.openhab.binding.freeboxos.internal.api.repeater.RepeaterManager;
+import org.openhab.binding.freeboxos.internal.api.system.SystemConf;
+import org.openhab.binding.freeboxos.internal.api.system.SystemManager;
+import org.openhab.binding.freeboxos.internal.api.vm.VmManager;
+import org.openhab.binding.freeboxos.internal.api.wifi.APManager;
+import org.openhab.binding.freeboxos.internal.api.wifi.AccessPointHost;
+import org.openhab.binding.freeboxos.internal.config.ClientConfiguration;
+import org.openhab.binding.freeboxos.internal.handler.FreeboxOsHandler;
+import org.openhab.core.config.discovery.AbstractDiscoveryService;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link FreeboxOsDiscoveryService} is responsible for discovering all things
+ * except the Freebox API thing itself
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class FreeboxOsDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
+ private final Logger logger = LoggerFactory.getLogger(FreeboxOsDiscoveryService.class);
+ private static final int DISCOVERY_TIME_SECONDS = 10;
+ private static final int BACKGROUND_SCAN_REFRESH_MINUTES = 1;
+
+ private @NonNullByDefault({}) FreeboxOsHandler bridgeHandler;
+ private @Nullable ScheduledFuture> backgroundFuture;
+
+ public FreeboxOsDiscoveryService() {
+ super(THINGS_TYPES_UIDS, DISCOVERY_TIME_SECONDS);
+ }
+
+ @Override
+ public void deactivate() {
+ super.deactivate();
+ }
+
+ @Override
+ public void setThingHandler(@Nullable ThingHandler handler) {
+ if (handler instanceof FreeboxOsHandler) {
+ bridgeHandler = (FreeboxOsHandler) handler;
+ activate(null);
+ }
+ }
+
+ @Override
+ public @Nullable ThingHandler getThingHandler() {
+ return bridgeHandler;
+ }
+
+ @Override
+ protected void startBackgroundDiscovery() {
+ stopBackgroundDiscovery();
+ backgroundFuture = scheduler.scheduleWithFixedDelay(this::startScan, BACKGROUND_SCAN_REFRESH_MINUTES,
+ BACKGROUND_SCAN_REFRESH_MINUTES, TimeUnit.MINUTES);
+ }
+
+ @Override
+ protected void stopBackgroundDiscovery() {
+ ScheduledFuture> future = this.backgroundFuture;
+ if (future != null) {
+ future.cancel(true);
+ this.backgroundFuture = null;
+ }
+ }
+
+ @Override
+ protected void startScan() {
+ logger.debug("Starting Freebox discovery scan");
+ if (bridgeHandler.getThing().getStatus() == ThingStatus.ONLINE) {
+ try {
+ ThingUID bridgeUID = bridgeHandler.getThing().getUID();
+ Map lanHosts = bridgeHandler.getManager(LanBrowserManager.class).getHostsMap();
+ discoverServer(bridgeUID);
+ discoverPhone(bridgeUID);
+ discoverRepeater(bridgeUID, lanHosts);
+ discoverPlayer(bridgeUID, lanHosts);
+ discoverVM(bridgeUID, lanHosts);
+ if (bridgeHandler.getConfiguration().discoverNetDevice) {
+ discoverHosts(bridgeUID, lanHosts);
+ }
+ } catch (FreeboxException e) {
+ logger.warn("Error while requesting data for things discovery : {}", e.getMessage());
+ }
+ }
+ }
+
+ private void discoverPhone(ThingUID bridgeUID) throws FreeboxException {
+ PhoneManager phoneManager = bridgeHandler.getManager(PhoneManager.class);
+ List statuses = phoneManager.getPhoneStatuses();
+ statuses.forEach(config -> {
+ ThingUID thingUID = new ThingUID(THING_TYPE_LANDLINE, bridgeUID, Long.toString(config.getId()));
+ logger.debug("Adding new Freebox Phone {} to inbox", thingUID);
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
+ .withProperty(ClientConfiguration.ID, config.getId()).withLabel(config.getType().name()).build();
+ thingDiscovered(discoveryResult);
+ });
+ }
+
+ private void discoverVM(ThingUID bridgeUID, Map lanHosts) throws FreeboxException {
+ VmManager vmManager = bridgeHandler.getManager(VmManager.class);
+ vmManager.getDevices().forEach(vm -> {
+ String mac = vm.getMac();
+ lanHosts.remove(mac);
+
+ ThingUID thingUID = new ThingUID(THING_TYPE_VM, bridgeUID, macToUid(mac));
+ logger.debug("Adding new VM Device {} to inbox", thingUID);
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
+ .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS)
+ .withLabel(String.format("%s (VM)", vm.getName())).withProperty(ClientConfiguration.ID, vm.getId())
+ .withProperty(Thing.PROPERTY_MAC_ADDRESS, mac).build();
+ thingDiscovered(discoveryResult);
+ });
+ }
+
+ private void discoverHosts(ThingUID bridgeUID, Map lanHosts) throws FreeboxException {
+ Map apHosts = bridgeHandler.getManager(APManager.class).getHostsMap();
+ Map repHosts = bridgeHandler.getManager(RepeaterManager.class).getHostsMap();
+ List wifiMacs = Stream.concat(apHosts.keySet().stream(), repHosts.keySet().stream())
+ .collect(Collectors.toList());
+
+ lanHosts.entrySet().forEach(entry -> {
+ LanHost lanHost = entry.getValue();
+ if (lanHost.isReachable()) {
+ String mac = entry.getKey();
+
+ ThingUID thingUID = new ThingUID(wifiMacs.contains(mac) ? THING_TYPE_WIFI_HOST : THING_TYPE_HOST,
+ bridgeUID, macToUid(mac));
+ logger.debug("Adding new Freebox Network Host {} to inbox", thingUID);
+ DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
+ .withProperty(Thing.PROPERTY_MAC_ADDRESS, mac).withTTL(300)
+ .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS)
+ .withLabel(lanHost.getPrimaryName().orElse(String.format("Freebox Network Device %s", mac)));
+ thingDiscovered(builder.build());
+ }
+ });
+ }
+
+ private void discoverRepeater(ThingUID bridgeUID, Map lanHosts) throws FreeboxException {
+ List repeaters = bridgeHandler.getManager(RepeaterManager.class).getDevices();
+ repeaters.forEach(repeater -> {
+ String mac = repeater.getMac();
+ lanHosts.remove(mac);
+
+ ThingUID thingUID = new ThingUID(THING_TYPE_REPEATER, bridgeUID, Integer.toString(repeater.getId()));
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
+ .withLabel(String.format("Repeater %s", repeater.getName()))
+ .withProperty(Thing.PROPERTY_MAC_ADDRESS, mac)
+ .withProperty(ClientConfiguration.ID, repeater.getId())
+ .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build();
+ thingDiscovered(discoveryResult);
+ });
+ }
+
+ private void discoverServer(ThingUID bridgeUID) throws FreeboxException {
+ SystemConf config = bridgeHandler.getManager(SystemManager.class).getConfig();
+
+ ThingTypeUID targetType = config.getBoardName().startsWith("fbxgw7") ? THING_TYPE_DELTA : THING_TYPE_REVOLUTION;
+ ThingUID thingUID = new ThingUID(targetType, bridgeUID, config.getSerial());
+ logger.debug("Adding new Freebox Server {} to inbox", thingUID);
+
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
+ .withProperty(Thing.PROPERTY_MAC_ADDRESS, config.getMac())
+ .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS)
+ .withLabel(config.getPrettyName().orElse("Freebox Server")).build();
+ thingDiscovered(discoveryResult);
+ }
+
+ private void discoverPlayer(ThingUID bridgeUID, Map lanHosts) throws FreeboxException {
+ PlayerManager playMgr = bridgeHandler.getManager(PlayerManager.class);
+ for (Player player : playMgr.getDevices()) {
+ lanHosts.remove(player.getMac());
+ ThingUID thingUID = new ThingUID(player.isApiAvailable() ? THING_TYPE_ACTIVE_PLAYER : THING_TYPE_PLAYER,
+ bridgeUID, Integer.toString(player.getId()));
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
+ .withLabel(player.getName()).withProperty(Thing.PROPERTY_MAC_ADDRESS, player.getMac())
+ .withProperty(ClientConfiguration.ID, player.getId())
+ .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build();
+ thingDiscovered(discoveryResult);
+ }
+ }
+
+ private String macToUid(String mac) {
+ return mac.replaceAll("[^A-Za-z0-9_]", "");
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ActivePlayerHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ActivePlayerHandler.java
new file mode 100644
index 0000000000000..a1ea1f8ba24e6
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ActivePlayerHandler.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2010-2021 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.PLAYER_STATUS;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.action.ActivePlayerActions;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.player.PlayerManager;
+import org.openhab.binding.freeboxos.internal.api.player.PlayerStatus;
+import org.openhab.binding.freeboxos.internal.api.system.DeviceConfig;
+import org.openhab.binding.freeboxos.internal.config.PlayerConfiguration;
+import org.openhab.core.audio.AudioHTTPServer;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The {@link ActivePlayerHandler} is responsible for handling everything associated to
+ * Freebox Player with api capabilities.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class ActivePlayerHandler extends PlayerHandler {
+
+ public ActivePlayerHandler(Thing thing, AudioHTTPServer audioHTTPServer, @Nullable String ipAddress,
+ BundleContext bundleContext) {
+ super(thing, audioHTTPServer, ipAddress, bundleContext);
+ }
+
+ @Override
+ protected void internalPoll() throws FreeboxException {
+ super.internalPoll();
+ fetchPlayerStatus();
+ }
+
+ private void fetchPlayerStatus() throws FreeboxException {
+ PlayerConfiguration config = getConfigAs(PlayerConfiguration.class);
+ PlayerManager playerManager = getManager(PlayerManager.class);
+ PlayerStatus status = playerManager.getPlayerStatus(config.id);
+ updateChannelString(PLAYER_STATUS, PLAYER_STATUS, status.getPowerState().name());
+ }
+
+ @Override
+ protected Optional getDeviceConfig() throws FreeboxException {
+ PlayerConfiguration config = getConfigAs(PlayerConfiguration.class);
+ PlayerManager playerManager = getManager(PlayerManager.class);
+ return Optional.ofNullable(playerManager.getConfig(config.id));
+ }
+
+ @Override
+ protected void internalCallReboot() throws FreeboxException {
+ PlayerConfiguration config = getConfigAs(PlayerConfiguration.class);
+ PlayerManager playerManager = getManager(PlayerManager.class);
+ playerManager.reboot(config.id);
+ }
+
+ @Override
+ public Collection> getServices() {
+ return Collections.singletonList(ActivePlayerActions.class);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/AirMediaSink.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/AirMediaSink.java
new file mode 100644
index 0000000000000..06dd0db639345
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/AirMediaSink.java
@@ -0,0 +1,162 @@
+/**
+ * Copyright (c) 2010-2021 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.core.audio.AudioFormat.*;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.airmedia.AirMediaActionData.MediaAction;
+import org.openhab.binding.freeboxos.internal.api.airmedia.AirMediaActionData.MediaType;
+import org.openhab.binding.freeboxos.internal.api.airmedia.AirMediaManager;
+import org.openhab.binding.freeboxos.internal.api.airmedia.MediaReceiverManager;
+import org.openhab.core.audio.AudioFormat;
+import org.openhab.core.audio.AudioHTTPServer;
+import org.openhab.core.audio.AudioSink;
+import org.openhab.core.audio.AudioStream;
+import org.openhab.core.audio.FixedLengthAudioStream;
+import org.openhab.core.audio.URLAudioStream;
+import org.openhab.core.audio.UnsupportedAudioFormatException;
+import org.openhab.core.audio.UnsupportedAudioStreamException;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.net.HttpServiceUtil;
+import org.openhab.core.thing.ThingStatus;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link AirMediaSink} is holding AudioSink capabilities for various
+ * things.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class AirMediaSink implements AudioSink {
+ private static final Set> SUPPORTED_STREAMS = Collections.singleton(AudioStream.class);
+ private static final Set BASIC_FORMATS = Set.of(WAV, OGG);
+ private static final Set ALL_MP3_FORMATS = Set.of(
+ new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 96000, null),
+ new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 112000, null),
+ new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 128000, null),
+ new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 160000, null),
+ new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 192000, null),
+ new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 224000, null),
+ new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 256000, null),
+ new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 320000, null));
+
+ private final Logger logger = LoggerFactory.getLogger(AirMediaSink.class);
+ private final ApiConsumerHandler thingHandler;
+ private final Set SUPPORTED_FORMATS = new HashSet<>();
+ private final AudioHTTPServer audioHTTPServer;
+ private final String callbackUrl;
+ private final String playerName;
+
+ public AirMediaSink(ApiConsumerHandler thingHandler, AudioHTTPServer audioHTTPServer, @Nullable String ipAddress,
+ BundleContext bundleContext, @Nullable String callbackUrl, String playerName) {
+ this.thingHandler = thingHandler;
+ this.audioHTTPServer = audioHTTPServer;
+ this.playerName = playerName;
+ if (callbackUrl != null && !callbackUrl.isEmpty()) {
+ this.callbackUrl = callbackUrl;
+ } else {
+ int port = HttpServiceUtil.getHttpServicePort(bundleContext);
+ if (port != -1 && ipAddress != null) {
+ // we do not use SSL as it can cause certificate validation issues.
+ this.callbackUrl = String.format("http://%s:%d", ipAddress, port);
+ } else {
+ throw new IllegalArgumentException(
+ "No network interface could be found or cannot find port of the http service.");
+ }
+ }
+ }
+
+ @Override
+ public Set> getSupportedStreams() {
+ return SUPPORTED_STREAMS;
+ }
+
+ @Override
+ public PercentType getVolume() throws IOException {
+ logger.debug("getVolume received but AirMedia does not have the capability - returning 100%.");
+ return PercentType.HUNDRED;
+ }
+
+ @Override
+ public void setVolume(PercentType volume) throws IOException {
+ logger.debug("setVolume received but AirMedia does not have the capability - ignoring it.");
+ }
+
+ @Override
+ public String getId() {
+ return thingHandler.getThing().getUID().toString();
+ }
+
+ @Override
+ public @Nullable String getLabel(@Nullable Locale locale) {
+ return thingHandler.getThing().getLabel();
+ }
+
+ @Override
+ public void process(@Nullable AudioStream audioStream)
+ throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
+ String name = this.playerName;
+ try {
+ AirMediaManager manager = thingHandler.getManager(AirMediaManager.class);
+ MediaReceiverManager receiver = thingHandler.getManager(MediaReceiverManager.class);
+ if (thingHandler.getThing().getStatus() == ThingStatus.ONLINE) {
+ String password = manager.getConfig().getPassword();
+ if (audioStream == null) {
+ receiver.sendToReceiver(name, password, MediaAction.STOP, MediaType.VIDEO);
+ } else {
+ String url = null;
+ if (audioStream instanceof URLAudioStream) {
+ // it is an external URL, we can access it directly
+ URLAudioStream urlAudioStream = (URLAudioStream) audioStream;
+ url = urlAudioStream.getURL();
+ } else {
+ // we serve it on our own HTTP server
+ url = callbackUrl + (audioStream instanceof FixedLengthAudioStream
+ ? audioHTTPServer.serve((FixedLengthAudioStream) audioStream, 20)
+ : audioHTTPServer.serve(audioStream));
+ }
+ logger.debug("AirPlay audio sink: process url {}", url);
+ receiver.sendToReceiver(name, password, MediaAction.STOP, MediaType.VIDEO);
+ receiver.sendToReceiver(name, password, MediaAction.START, MediaType.VIDEO, url);
+ }
+ }
+ } catch (FreeboxException e) {
+ logger.warn("Audio stream playback failed: {}", e.getMessage());
+ }
+ }
+
+ @Override
+ public Set getSupportedFormats() {
+ if (SUPPORTED_FORMATS.isEmpty()) {
+ SUPPORTED_FORMATS.addAll(BASIC_FORMATS);
+ // if (getConfigAs(PlayerConfiguration.class).acceptAllMp3) {
+ SUPPORTED_FORMATS.addAll(ALL_MP3_FORMATS);
+ // } else { // Only accept MP3 bitrates >= 96 kbps
+ // SUPPORTED_FORMATS.add(MP3);
+ // }
+ }
+ return SUPPORTED_FORMATS;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ApiConsumerHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ApiConsumerHandler.java
new file mode 100644
index 0000000000000..239b29f1dfc56
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ApiConsumerHandler.java
@@ -0,0 +1,207 @@
+/**
+ * Copyright (c) 2010-2021 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 java.time.ZonedDateTime;
+import java.util.Map;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import javax.measure.Unit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.rest.RestManager;
+import org.openhab.binding.freeboxos.internal.config.ApiConsumerConfiguration;
+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.QuantityType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingStatusInfo;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.BridgeHandler;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link ServerHandler} handle common parts of Freebox bridges.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+abstract class ApiConsumerHandler extends BaseThingHandler {
+ private final Logger logger = LoggerFactory.getLogger(ApiConsumerHandler.class);
+
+ private @Nullable ScheduledFuture> globalJob;
+ private @Nullable FreeboxOsHandler bridgeHandler;
+
+ ApiConsumerHandler(Thing thing) {
+ super(thing);
+ }
+
+ public T getManager(Class classOfT) throws FreeboxException {
+ FreeboxOsHandler handler = bridgeHandler;
+ if (handler == null) {
+ throw new FreeboxException("bridge handler not yet defined");
+ }
+ return handler.getManager(classOfT);
+ }
+
+ @Override
+ public void initialize() {
+ logger.debug("Initializing handler for thing {}", getThing().getUID());
+ if (checkBridgeHandler()) {
+ Map properties = editProperties();
+ if (editProperties().size() == 0) {
+ try {
+ internalGetProperties(properties);
+ updateProperties(properties);
+ } catch (FreeboxException e) {
+ logger.warn("Error getting thing properties : {}", e.getMessage());
+ }
+ }
+ startRefreshJob();
+ }
+ }
+
+ abstract void internalGetProperties(Map properties) throws FreeboxException;
+
+ @Override
+ public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
+ logger.debug("Thing {}: bridge status changed to {}", getThing().getUID(), bridgeStatusInfo);
+ if (checkBridgeHandler()) {
+ startRefreshJob();
+ } else {
+ stopRefreshJob();
+ }
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (command instanceof RefreshType || getThing().getStatus() != ThingStatus.ONLINE) {
+ return;
+ }
+ try {
+ if (bridgeHandler == null || !internalHandleCommand(channelUID, command)) {
+ logger.debug("Unexpected command {} on channel {}", command, channelUID.getId());
+ }
+ } catch (FreeboxException e) {
+ logger.warn("Error handling command : {}", e.getMessage());
+ }
+ }
+
+ private boolean checkBridgeHandler() {
+ Bridge bridge = getBridge();
+ if (bridge != null) {
+ BridgeHandler handler = bridge.getHandler();
+ if (handler instanceof FreeboxOsHandler) {
+ if (bridge.getStatus() == ThingStatus.ONLINE) {
+ bridgeHandler = (FreeboxOsHandler) handler;
+ updateStatus(ThingStatus.ONLINE);
+ return true;
+ }
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_MISSING_ERROR);
+ }
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+ }
+ return false;
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Disposing handler for thing {}", getThing().getUID());
+ stopRefreshJob();
+ super.dispose();
+ }
+
+ private void startRefreshJob() {
+ ScheduledFuture> job = globalJob;
+ if (job == null || job.isCancelled()) {
+ int refreshInterval = getConfigAs(ApiConsumerConfiguration.class).refreshInterval;
+ logger.debug("Scheduling state update every {} seconds for thing {}...", refreshInterval,
+ getThing().getUID());
+ globalJob = scheduler.scheduleWithFixedDelay(() -> {
+ try {
+ internalPoll();
+ } catch (FreeboxException e) {
+ logger.warn("Error polling thing {} : {}", getThing().getUID(), e.getMessage());
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+ }
+ }, 0, refreshInterval, TimeUnit.SECONDS);
+ }
+ }
+
+ protected void stopRefreshJob() {
+ ScheduledFuture> job = globalJob;
+ if (job != null && !job.isCancelled()) {
+ logger.debug("Stop scheduled state update for thing {}", getThing().getUID());
+ job.cancel(true);
+ globalJob = null;
+ }
+ }
+
+ protected boolean internalHandleCommand(ChannelUID channelUID, Command command) throws FreeboxException {
+ return false;
+ }
+
+ protected abstract void internalPoll() throws FreeboxException;
+
+ private void updateIfActive(String group, String channelId, State state) {
+ ChannelUID id = new ChannelUID(getThing().getUID(), group, channelId);
+ if (isLinked(id)) {
+ updateState(id, state);
+ }
+ }
+
+ protected void updateChannelDateTimeState(String group, String channelId, @Nullable ZonedDateTime timestamp) {
+ updateIfActive(group, channelId, timestamp == null ? UnDefType.NULL : new DateTimeType(timestamp));
+ }
+
+ protected void updateChannelOnOff(String group, String channelId, boolean value) {
+ updateIfActive(group, channelId, OnOffType.from(value));
+ }
+
+ protected void updateChannelString(String group, String channelId, @Nullable String value) {
+ updateIfActive(group, channelId, value != null ? new StringType(value) : UnDefType.NULL);
+ }
+
+ protected void updateChannelQuantity(String group, String channelId, double d, Unit> unit) {
+ updateChannelQuantity(group, channelId, new QuantityType<>(d, unit));
+ }
+
+ protected void updateChannelQuantity(String group, String channelId, @Nullable QuantityType> quantity) {
+ updateIfActive(group, channelId, quantity != null ? quantity : UnDefType.NULL);
+ }
+
+ protected void updateChannelDecimal(String group, String channelId, @Nullable Integer value) {
+ updateIfActive(group, channelId, value != null ? new DecimalType(value) : UnDefType.NULL);
+ }
+
+ protected void updateChannelQuantity(String group, String channelId, QuantityType> qtty, Unit> unit) {
+ updateChannelQuantity(group, channelId, qtty.toUnit(unit));
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/FreeDeviceHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/FreeDeviceHandler.java
new file mode 100644
index 0000000000000..015453770554a
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/FreeDeviceHandler.java
@@ -0,0 +1,170 @@
+/**
+ * Copyright (c) 2010-2021 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.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.airmedia.AirMediaActionData.MediaType;
+import org.openhab.binding.freeboxos.internal.api.lan.NameSource;
+import org.openhab.binding.freeboxos.internal.api.system.DeviceConfig;
+import org.openhab.binding.freeboxos.internal.api.system.Sensor;
+import org.openhab.binding.freeboxos.internal.api.system.Sensor.SensorKind;
+import org.openhab.core.audio.AudioHTTPServer;
+import org.openhab.core.audio.AudioSink;
+import org.openhab.core.library.CoreItemFactory;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.builder.ChannelBuilder;
+import org.openhab.core.thing.type.ChannelTypeUID;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public abstract class FreeDeviceHandler extends HostHandler {
+ private final Logger logger = LoggerFactory.getLogger(FreeDeviceHandler.class);
+ private final AudioHTTPServer audioHTTPServer;
+ private final BundleContext bundleContext;
+
+ private long uptime = -1;
+
+ private @Nullable ServiceRegistration reg;
+ private @Nullable String ohIP;
+
+ public FreeDeviceHandler(Thing thing, AudioHTTPServer audioHTTPServer, @Nullable String ipAddress,
+ BundleContext bundleContext) {
+ super(thing);
+ this.audioHTTPServer = audioHTTPServer;
+ this.ohIP = ipAddress;
+ this.bundleContext = bundleContext;
+ }
+
+ @Override
+ void internalGetProperties(Map properties) throws FreeboxException {
+ super.internalGetProperties(properties);
+ getDeviceConfig().ifPresent(config -> {
+ List channels = new ArrayList<>(getThing().getChannels());
+ List sensors = config.getAllSensors();
+ sensors.forEach(sensor -> {
+ ChannelUID sensorId = new ChannelUID(thing.getUID(), GROUP_SENSORS, sensor.getId());
+ if (channels.stream().noneMatch(c -> c.getUID().equals(sensorId))) {
+ String channelLabel = sensor.getName().startsWith(sensor.getKind().getLabel()) ? sensor.getName()
+ : String.format("%s %s", sensor.getKind().getLabel(), sensor.getName());
+ ChannelBuilder channelBuilder = ChannelBuilder.create(sensorId).withLabel(channelLabel);
+ if (sensor.getKind() == SensorKind.FAN) {
+ channels.add(channelBuilder.withAcceptedItemType(CoreItemFactory.NUMBER)
+ .withType(new ChannelTypeUID(BINDING_ID + ":fanspeed")).build());
+ } else if (sensor.getKind() == SensorKind.TEMP) {
+ channels.add(channelBuilder.withAcceptedItemType("Number:Temperature")
+ .withType(new ChannelTypeUID(BINDING_ID + ":temperature")).build());
+ }
+ }
+ });
+ updateThing(editThing().withChannels(channels).build());
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void initialize() {
+ super.initialize();
+ Map properties = editProperties();
+ String upnpName = properties.get(NameSource.UPNP.name());
+ if (upnpName != null && Boolean.parseBoolean(properties.get(MediaType.AUDIO.name()))) {
+ reg = (ServiceRegistration) bundleContext.registerService(AudioSink.class.getName(),
+ new AirMediaSink(this, audioHTTPServer, ohIP, bundleContext, "", upnpName), new Hashtable<>());
+ }
+ }
+
+ @Override
+ public void dispose() {
+ if (reg != null) {
+ reg.unregister();
+ }
+ super.dispose();
+ }
+
+ @Override
+ protected void internalPoll() throws FreeboxException {
+ super.internalPoll();
+ fetchSystemConfig();
+ }
+
+ protected abstract Optional getDeviceConfig() throws FreeboxException;
+
+ protected abstract void internalCallReboot() throws FreeboxException;
+
+ private void fetchSystemConfig() throws FreeboxException {
+ getDeviceConfig().ifPresent(config -> {
+ List sensors = config.getAllSensors();
+ sensors.forEach(sensor -> {
+ switch (sensor.getKind()) {
+ case FAN:
+ updateChannelDecimal(GROUP_SENSORS, sensor.getId(), sensor.getValue());
+ break;
+ case TEMP:
+ updateChannelQuantity(GROUP_SENSORS, sensor.getId(), sensor.getValue(), SIUnits.CELSIUS);
+ break;
+ case UNKNOWN:
+ logger.warn("Unknown sensor kind : {}", sensor);
+ break;
+ }
+ });
+
+ long newUptime = config.getUptimeVal();
+ if (newUptime < uptime) {
+ triggerChannel(new ChannelUID(getThing().getUID(), SYS_INFO, BOX_EVENT), "restarted");
+ Map properties = editProperties();
+ if (!config.getFirmwareVersion().equals(properties.get(Thing.PROPERTY_FIRMWARE_VERSION))) {
+ properties.put(Thing.PROPERTY_FIRMWARE_VERSION, config.getFirmwareVersion());
+ triggerChannel(new ChannelUID(getThing().getUID(), SYS_INFO, BOX_EVENT), "firmware_updated");
+ updateProperties(properties);
+ }
+ }
+ uptime = newUptime;
+ updateChannelQuantity(SYS_INFO, UPTIME, uptime, Units.SECOND);
+ });
+ }
+
+ public void reboot() {
+ try {
+ internalCallReboot();
+ triggerChannel(new ChannelUID(getThing().getUID(), SYS_INFO, BOX_EVENT), "reboot_requested");
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DUTY_CYCLE, "System rebooting, will wait 2 minutes.");
+ stopRefreshJob();
+ scheduler.schedule(this::initialize, 2, TimeUnit.MINUTES);
+ } catch (FreeboxException e) {
+ logger.warn("Error rebooting device : {}", e.getMessage());
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/FreeboxOsHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/FreeboxOsHandler.java
new file mode 100644
index 0000000000000..3939422250be4
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/FreeboxOsHandler.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2010-2021 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 java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.Future;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
+import org.openhab.binding.freeboxos.internal.api.rest.RestManager;
+import org.openhab.binding.freeboxos.internal.config.FreeboxOsConfiguration;
+import org.openhab.binding.freeboxos.internal.discovery.FreeboxOsDiscoveryService;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseBridgeHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.openhab.core.types.Command;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link FreeboxOsHandler} handle common parts of Freebox bridges.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class FreeboxOsHandler extends BaseBridgeHandler {
+ private final Logger logger = LoggerFactory.getLogger(FreeboxOsHandler.class);
+
+ private @Nullable Future> openConnectionJob;
+ private final FreeboxOsSession session;
+
+ public FreeboxOsHandler(Bridge thing, FreeboxOsSession session) {
+ super(thing);
+ this.session = session;
+ }
+
+ @Override
+ public void initialize() {
+ logger.debug("Initializing Freebox OS API handler for thing {}.", getThing().getUID());
+ Future> job = openConnectionJob;
+ if (job == null || job.isCancelled()) {
+ openConnectionJob = scheduler.submit(() -> {
+ try {
+ FreeboxOsConfiguration config = getConfiguration();
+
+ updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING,
+ "Please accept pairing request directly on your freebox");
+
+ String currentAppToken = session.initialize(config);
+
+ if (!currentAppToken.equals(config.appToken)) {
+ Configuration thingConfig = editConfiguration();
+ thingConfig.put(FreeboxOsConfiguration.APP_TOKEN, currentAppToken);
+ updateConfiguration(thingConfig);
+ logger.info(
+ "App token updated in Configuration, please give needed permissions to openHAB app in your Freebox management console");
+ }
+
+ updateStatus(ThingStatus.ONLINE);
+ } catch (FreeboxException e) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
+ }
+ });
+ }
+ }
+
+ public T getManager(Class classOfT) throws FreeboxException {
+ return session.getManager(classOfT);
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Disposing Freebox OS API handler for thing {}", getThing().getUID());
+ Future> job = openConnectionJob;
+ if (job != null && !job.isCancelled()) {
+ job.cancel(true);
+ openConnectionJob = null;
+ }
+ session.closeSession();
+ super.dispose();
+ }
+
+ @Override
+ public Collection> getServices() {
+ return Collections.singleton(FreeboxOsDiscoveryService.class);
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ }
+
+ public FreeboxOsConfiguration getConfiguration() {
+ return getConfigAs(FreeboxOsConfiguration.class);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/HostHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/HostHandler.java
new file mode 100644
index 0000000000000..a5a00208a4649
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/HostHandler.java
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2010-2021 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.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.action.HostActions;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.airmedia.MediaReceiverManager;
+import org.openhab.binding.freeboxos.internal.api.lan.ConnectivityData;
+import org.openhab.binding.freeboxos.internal.api.lan.LanBrowserManager;
+import org.openhab.binding.freeboxos.internal.api.lan.NameSource;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link HostHandler} is responsible for handling everything associated to
+ * any Freebox thing types except the bridge thing type.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class HostHandler extends ApiConsumerHandler {
+ private final Logger logger = LoggerFactory.getLogger(HostHandler.class);
+ private @Nullable String ipAddress;
+
+ public HostHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ void internalGetProperties(Map properties) throws FreeboxException {
+ getManager(LanBrowserManager.class).getHost(getMac()).ifPresent(host -> {
+ host.getPrimaryName().ifPresent(name -> properties.put(NameSource.UPNP.name(), name));
+ properties.put(Thing.PROPERTY_VENDOR, host.getVendorName().orElse("Unknown"));
+ });
+ String name = properties.get(NameSource.UPNP.name());
+ if (name != null) {
+ getManager(MediaReceiverManager.class).getDevices().stream().filter(r -> name.equals(r.getName()))
+ .findFirst().ifPresent(receiver -> {
+ receiver.getCapabilities().entrySet()
+ .forEach(entry -> properties.put(entry.getKey().name(), entry.getValue().toString()));
+ });
+ }
+ }
+
+ @Override
+ protected void internalPoll() throws FreeboxException {
+ ConnectivityData lanHost = fetchConnectivity();
+ ipAddress = lanHost.getIpv4();
+ updateChannelOnOff(CONNECTIVITY, REACHABLE, lanHost.isReachable());
+ updateChannelDateTimeState(CONNECTIVITY, LAST_SEEN, lanHost.getLastSeen());
+ updateChannelString(CONNECTIVITY, IP_ADDRESS, ipAddress);
+ updateStatus(lanHost.isReachable() ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
+ }
+
+ protected ConnectivityData fetchConnectivity() throws FreeboxException {
+ return getManager(LanBrowserManager.class).getHost(getMac())
+ .orElseThrow(() -> new FreeboxException("Host data not found"));
+ }
+
+ public @Nullable String getIpAddress() {
+ return ipAddress;
+ }
+
+ public void wol() {
+ try {
+ getManager(LanBrowserManager.class).wakeOnLan(getMac());
+ } catch (FreeboxException e) {
+ logger.warn("Error waking up host : {}", e.getMessage());
+ }
+ }
+
+ @Override
+ public Collection> getServices() {
+ return Collections.singletonList(HostActions.class);
+ }
+
+ protected String getMac() {
+ return ((String) getConfig().get(Thing.PROPERTY_MAC_ADDRESS)).toLowerCase();
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/LandlineHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/LandlineHandler.java
new file mode 100644
index 0000000000000..ce6ea5594c939
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/LandlineHandler.java
@@ -0,0 +1,147 @@
+/**
+ * Copyright (c) 2010-2021 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.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.call.CallEntry;
+import org.openhab.binding.freeboxos.internal.api.call.CallEntry.CallType;
+import org.openhab.binding.freeboxos.internal.api.call.CallManager;
+import org.openhab.binding.freeboxos.internal.api.phone.PhoneConfig;
+import org.openhab.binding.freeboxos.internal.api.phone.PhoneManager;
+import org.openhab.binding.freeboxos.internal.api.phone.PhoneStatus;
+import org.openhab.binding.freeboxos.internal.config.ClientConfiguration;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.types.Command;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link LandlineHandler} is responsible for handling everything associated to
+ * to the phone landline associated with the Freebox Server.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class LandlineHandler extends ApiConsumerHandler {
+ private final static String LAST_CALL_TIMESTAMP = "last-call-timestamp";
+ private final Logger logger = LoggerFactory.getLogger(LandlineHandler.class);
+ private ZonedDateTime lastCallTimestamp = ZonedDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC);
+
+ public LandlineHandler(Thing thing) {
+ super(thing);
+ String lastCall = thing.getProperties().get(LAST_CALL_TIMESTAMP);
+ if (lastCall != null) {
+ lastCallTimestamp = ZonedDateTime.parse(lastCall).minusSeconds(1);
+ }
+ }
+
+ @Override
+ void internalGetProperties(Map properties) throws FreeboxException {
+ PhoneManager phoneManager = getManager(PhoneManager.class);
+ List phones = phoneManager.getPhoneStatuses();
+ phones.stream().filter(phone -> phone.getId() == getConfigAs(ClientConfiguration.class).id).findFirst()
+ .ifPresent(config -> properties.put(PHONE_TYPE, config.getType().name()));
+ }
+
+ @Override
+ protected void internalPoll() throws FreeboxException {
+ pollStatus();
+ pollCalls();
+ updateStatus(ThingStatus.ONLINE);
+ }
+
+ private void pollCalls() throws FreeboxException {
+ logger.debug("Polling phone calls since last...");
+ CallManager callManager = getManager(CallManager.class);
+
+ callManager.getCallEntries().stream().filter(entry -> entry.getDatetime() != null)
+ .sorted(Comparator.comparing(CallEntry::getDatetime))
+ .filter(c -> lastCallTimestamp.isBefore(c.getDatetime())).forEach(call -> {
+ if (call.getType() == CallType.INCOMING) {
+ triggerChannel(new ChannelUID(getThing().getUID(), STATE, PHONE_EVENT),
+ "incoming_call#" + call.getPhoneNumber());
+ } else {
+ updateCallChannels(call);
+ ZonedDateTime timeStamp = call.getDatetime();
+ if (timeStamp != null) {
+ lastCallTimestamp = timeStamp;
+ updateProperty(LAST_CALL_TIMESTAMP, timeStamp.toString());
+ }
+ }
+ });
+ }
+
+ private void pollStatus() throws FreeboxException {
+ logger.debug("Polling phone status...");
+ PhoneManager phoneManager = getManager(PhoneManager.class);
+
+ phoneManager.getStatus(getConfigAs(ClientConfiguration.class).id).ifPresent(status -> {
+ updateChannelOnOff(STATE, ONHOOK, status.isOnHook());
+ updateChannelOnOff(STATE, RINGING, status.isRinging());
+ });
+
+ PhoneConfig config = phoneManager.getConfig();
+ updateChannelOnOff(PHONE_MISC, ALTERNATE_RING, config.isDectRingOnOff());
+ updateChannelOnOff(PHONE_MISC, DECT_ACTIVE, config.isEnabled());
+ }
+
+ private void updateCallChannels(CallEntry call) {
+ String group = call.getType().name().toLowerCase();
+ String phoneNumber = call.getPhoneNumber();
+
+ ChannelUID id = new ChannelUID(getThing().getUID(), group, NUMBER);
+ updateState(id, new StringType(call.getPhoneNumber()));
+ updateChannelDateTimeState(group, TIMESTAMP, call.getDatetime());
+ if (call.getType() != CallType.MISSED) { // Missed call have no duration by definition
+ updateChannelQuantity(group, DURATION, call.getDuration(), Units.SECOND);
+ }
+ if (phoneNumber != null && !phoneNumber.equals(call.getName())) {
+ updateChannelString(group, NAME, call.getPhoneNumber());
+ }
+ }
+
+ @Override
+ protected boolean internalHandleCommand(ChannelUID channelUID, Command command) throws FreeboxException {
+ PhoneManager phoneManager = getManager(PhoneManager.class);
+ String target = channelUID.getIdWithoutGroup();
+ if (command instanceof OnOffType) {
+ boolean status = (OnOffType) command == OnOffType.ON;
+ if (RINGING.equals(target)) {
+ phoneManager.ring(status);
+ return true;
+ } else if (DECT_ACTIVE.equals(target)) {
+ phoneManager.setStatus(status);
+ return true;
+ } else if (ALTERNATE_RING.equals(target)) {
+ phoneManager.alternateRing(status);
+ return true;
+ }
+ }
+ return super.internalHandleCommand(channelUID, command);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/PlayerHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/PlayerHandler.java
new file mode 100644
index 0000000000000..490ffa8ab101d
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/PlayerHandler.java
@@ -0,0 +1,147 @@
+/**
+ * Copyright (c) 2010-2021 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.KEY_CODE;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.action.PlayerActions;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.player.Player;
+import org.openhab.binding.freeboxos.internal.api.player.PlayerManager;
+import org.openhab.binding.freeboxos.internal.api.system.DeviceConfig;
+import org.openhab.binding.freeboxos.internal.config.PlayerConfiguration;
+import org.openhab.core.audio.AudioHTTPServer;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.openhab.core.types.Command;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link PlayerHandler} is responsible for handling everything associated to
+ * any Freebox Player thing type.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ * https://github.com/betonniere/freeteuse/
+ * https://github.com/MaximeCheramy/remotefreebox/blob/16e2a42ed7cfcfd1ab303184280564eeace77919/remotefreebox/fbx_descriptor.py
+ * https://dev.freebox.fr/sdk/freebox_player_1.1.4_codes.html
+ * http://192.168.0.98/pub/remote_control?code=78952520&key=1&long=true
+ */
+@NonNullByDefault
+public class PlayerHandler extends FreeDeviceHandler {
+ private static final List VALID_REMOTE_KEYS = Arrays.asList("red", "green", "blue", "yellow", "power",
+ "list", "tv", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "vol_inc", "vol_dec", "mute", "prgm_inc",
+ "prgm_dec", "prev", "bwd", "play", "rec", "fwd", "next", "up", "right", "down", "left", "back", "swap",
+ "info", "epg", "mail", "media", "help", "options", "pip", "ok", "home");
+
+ private final Logger logger = LoggerFactory.getLogger(PlayerHandler.class);
+
+ public PlayerHandler(Thing thing, AudioHTTPServer audioHTTPServer, @Nullable String ipAddress,
+ BundleContext bundleContext) {
+ super(thing, audioHTTPServer, ipAddress, bundleContext);
+ }
+
+ @Override
+ void internalGetProperties(Map properties) throws FreeboxException {
+ super.internalGetProperties(properties);
+ for (Player player : getManager(PlayerManager.class).getDevices()) {
+ if (player.getMac().equals(getMac())) {
+ properties.put(Thing.PROPERTY_MODEL_ID, player.getModel());
+ if (player.isApiAvailable() && player.isReachable()) {
+ DeviceConfig config = getManager(PlayerManager.class).getConfig(player.getId());
+ properties.put(Thing.PROPERTY_SERIAL_NUMBER, config.getSerial());
+ properties.put(Thing.PROPERTY_FIRMWARE_VERSION, config.getFirmwareVersion());
+ }
+ }
+ }
+ }
+
+ // private String getPassword() {
+ // return (String) getConfig().get(PlayerConfiguration.PASSWORD);
+ // }
+
+ @Override
+ protected boolean internalHandleCommand(ChannelUID channelUID, Command command) throws FreeboxException {
+ if (KEY_CODE.equals(channelUID.getIdWithoutGroup()) && command instanceof StringType) {
+ sendKey(command.toString(), false, 1);
+ return true;
+ }
+
+ return super.internalHandleCommand(channelUID, command);
+ }
+
+ public void sendKey(String key, boolean longPress, int count) {
+ String aKey = key.toLowerCase();
+ String ip = getIpAddress();
+ if (ip == null) {
+ logger.info("Player IP is unknown");
+ } else if (VALID_REMOTE_KEYS.contains(aKey)) {
+ String remoteCode = (String) getConfig().get(PlayerConfiguration.REMOTE_CODE);
+ if (remoteCode != null) {
+ UriBuilder uriBuilder = UriBuilder.fromPath("pub").scheme("http").host(ip).path("remote_control");
+ uriBuilder.queryParam("code", remoteCode).queryParam("key", aKey);
+ if (longPress) {
+ uriBuilder.queryParam("long", true);
+ }
+ if (count > 1) {
+ uriBuilder.queryParam("repeat", count);
+ }
+ // try {
+ // TODO : s Correct this
+ // getApi().execute(uriBuilder.build(), HttpMethod.GET, null, null, false);
+ // } catch (FreeboxException e) {
+ // logger.warn("Error calling Player url : {}", e.getMessage());
+ // }
+ } else {
+ logger.warn("A remote code must be configured in the on the player thing.");
+ }
+ } else {
+ logger.info("Key '{}' is not a valid key expression", key);
+ }
+ }
+
+ public void sendMultipleKeys(String keys) {
+ String[] keyChain = keys.split(",");
+ Arrays.stream(keyChain).forEach(key -> {
+ sendKey(key, false, 1);
+ });
+ }
+
+ @Override
+ public Collection> getServices() {
+ return Collections.singletonList(PlayerActions.class);
+ }
+
+ @Override
+ protected Optional getDeviceConfig() throws FreeboxException {
+ return Optional.empty();
+ }
+
+ @Override
+ protected void internalCallReboot() throws FreeboxException {
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/RepeaterHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/RepeaterHandler.java
new file mode 100644
index 0000000000000..49699f93a81af
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/RepeaterHandler.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2010-2021 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.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.lan.LanHost;
+import org.openhab.binding.freeboxos.internal.api.repeater.Repeater;
+import org.openhab.binding.freeboxos.internal.api.repeater.RepeaterManager;
+import org.openhab.binding.freeboxos.internal.config.ClientConfiguration;
+import org.openhab.core.thing.Thing;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link RepeaterHandler} is responsible for interface to a freebox
+ * pop repeater.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class RepeaterHandler extends HostHandler {
+ private final Logger logger = LoggerFactory.getLogger(RepeaterHandler.class);
+
+ public RepeaterHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ void internalGetProperties(Map properties) throws FreeboxException {
+ super.internalGetProperties(properties);
+ getManager(RepeaterManager.class).getDevices().stream().filter(rep -> rep.getMac().equals(getMac()))
+ .forEach(repeater -> {
+ properties.put(Thing.PROPERTY_SERIAL_NUMBER, repeater.getSerial());
+ properties.put(Thing.PROPERTY_FIRMWARE_VERSION, repeater.getFirmwareVersion());
+ properties.put(Thing.PROPERTY_MODEL_ID, repeater.getModel());
+ });
+ }
+
+ @Override
+ protected void internalPoll() throws FreeboxException {
+ super.internalPoll();
+ logger.debug("Polling Repeater status");
+ RepeaterManager repeaterManager = getManager(RepeaterManager.class);
+
+ ClientConfiguration config = getConfigAs(ClientConfiguration.class);
+ List hosts = repeaterManager.getRepeaterHosts(config.id);
+ updateChannelDecimal(REPEATER_MISC, HOST_COUNT, hosts.size());
+
+ Repeater repeater = repeaterManager.getDevice(config.id);
+ updateChannelDateTimeState(REPEATER_MISC, RPT_TIMESTAMP, repeater.getBootTime());
+ updateChannelOnOff(REPEATER_MISC, LED, repeater.getLedActivated());
+ updateChannelString(REPEATER_MISC, CONNECTION_STATUS, repeater.getConnection());
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/RevolutionHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/RevolutionHandler.java
new file mode 100644
index 0000000000000..929d20465dbdf
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/RevolutionHandler.java
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2010-2021 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 static org.openhab.core.library.unit.Units.PERCENT;
+
+import java.util.concurrent.Callable;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.lcd.LcdConfig;
+import org.openhab.binding.freeboxos.internal.api.lcd.LcdManager;
+import org.openhab.core.audio.AudioHTTPServer;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.IncreaseDecreaseType;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.Command;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link RevolutionHandler} is responsible for handling take care of
+ * revolution server specifics
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class RevolutionHandler extends ServerHandler {
+ private final Logger logger = LoggerFactory.getLogger(RevolutionHandler.class);
+
+ public RevolutionHandler(Thing thing, AudioHTTPServer audioHTTPServer, @Nullable String ipAddress,
+ BundleContext bundleContext) {
+ super(thing, audioHTTPServer, ipAddress, bundleContext);
+ }
+
+ @Override
+ protected boolean internalHandleCommand(ChannelUID channelUID, Command command) throws FreeboxException {
+ LcdManager manager = getManager(LcdManager.class);
+ LcdConfig config = manager.getConfig();
+ switch (channelUID.getIdWithoutGroup()) {
+ case LCD_BRIGHTNESS:
+ setBrightness(manager, config, command);
+ internalPoll();
+ return true;
+ case LCD_ORIENTATION:
+ setOrientation(manager, config, command);
+ internalPoll();
+ return true;
+ case LCD_FORCED:
+ setForced(manager, config, command);
+ internalPoll();
+ return true;
+ }
+ return super.internalHandleCommand(channelUID, command);
+ }
+
+ @Override
+ protected void internalPoll() throws FreeboxException {
+ super.internalPoll();
+ LcdConfig config = getManager(LcdManager.class).getConfig();
+ updateChannelQuantity(DISPLAY, LCD_BRIGHTNESS, config.getBrightness(), PERCENT);
+ updateChannelDecimal(DISPLAY, LCD_ORIENTATION, config.getOrientation());
+ updateChannelOnOff(DISPLAY, LCD_FORCED, config.isOrientationForced());
+ }
+
+ private void setOrientation(LcdManager manager, LcdConfig config, Command command) throws FreeboxException {
+ if (command instanceof DecimalType) {
+ config.setOrientation(((DecimalType) command).intValue());
+ manager.setConfig(config);
+ } else {
+ logger.warn("Invalid command {} from channel {}", command, LCD_ORIENTATION);
+ }
+ }
+
+ private void setForced(LcdManager manager, LcdConfig config, Command command) throws FreeboxException {
+ if (ON_OFF_CLASSES.contains(command.getClass())) {
+ config.setOrientationForced(TRUE_COMMANDS.contains(command));
+ manager.setConfig(config);
+ } else {
+ logger.warn("Invalid command {} from channel {}", command, LCD_FORCED);
+ }
+ }
+
+ private void setBrightness(LcdManager manager, LcdConfig config, Command command) throws FreeboxException {
+ if (command instanceof IncreaseDecreaseType) {
+ changeLcdBrightness(() -> config.getBrightness() + (command == IncreaseDecreaseType.INCREASE ? 1 : -1));
+ } else if (command instanceof OnOffType) {
+ changeLcdBrightness(() -> command == OnOffType.ON ? 100 : 0);
+ } else if (command instanceof QuantityType) {
+ changeLcdBrightness(() -> ((QuantityType>) command).intValue());
+ } else if (command instanceof DecimalType || command instanceof PercentType) {
+ changeLcdBrightness(() -> ((DecimalType) command).intValue());
+ } else {
+ logger.warn("Invalid command {} from channel {}", command, LCD_BRIGHTNESS);
+ }
+ }
+
+ private void changeLcdBrightness(Callable function) throws FreeboxException {
+ LcdManager manager = getManager(LcdManager.class);
+ LcdConfig config = manager.getConfig();
+ try {
+ int newValue = function.call();
+ config.setBrightness(newValue);
+ manager.setConfig(config);
+ } catch (Exception e) {
+ throw new FreeboxException(e, "Error setting brightness");
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ServerHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ServerHandler.java
new file mode 100644
index 0000000000000..0ce162f16d07f
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ServerHandler.java
@@ -0,0 +1,199 @@
+/**
+ * Copyright (c) 2010-2021 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 static org.openhab.core.library.unit.Units.*;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.action.ServerActions;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.airmedia.AirMediaManager;
+import org.openhab.binding.freeboxos.internal.api.connection.ConnectionManager;
+import org.openhab.binding.freeboxos.internal.api.connection.ConnectionStatus;
+import org.openhab.binding.freeboxos.internal.api.ftp.FtpManager;
+import org.openhab.binding.freeboxos.internal.api.lan.ConnectivityData;
+import org.openhab.binding.freeboxos.internal.api.lan.LanConfig;
+import org.openhab.binding.freeboxos.internal.api.lan.LanConfig.NetworkMode;
+import org.openhab.binding.freeboxos.internal.api.lan.LanManager;
+import org.openhab.binding.freeboxos.internal.api.lan.NameSource;
+import org.openhab.binding.freeboxos.internal.api.netshare.NetShareManager.SambaManager;
+import org.openhab.binding.freeboxos.internal.api.netshare.SambaConfig;
+import org.openhab.binding.freeboxos.internal.api.system.DeviceConfig;
+import org.openhab.binding.freeboxos.internal.api.system.SystemConf;
+import org.openhab.binding.freeboxos.internal.api.system.SystemManager;
+import org.openhab.binding.freeboxos.internal.api.upnpav.UPnPAVManager;
+import org.openhab.binding.freeboxos.internal.api.wifi.WifiManager;
+import org.openhab.core.audio.AudioHTTPServer;
+import org.openhab.core.library.dimension.DataTransferRate;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.openhab.core.types.Command;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link ServerHandler} handle common parts of Freebox bridges.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class ServerHandler extends FreeDeviceHandler {
+ private final static BigDecimal HUNDRED = BigDecimal.valueOf(100);
+ private final Logger logger = LoggerFactory.getLogger(ServerHandler.class);
+
+ public ServerHandler(Thing thing, AudioHTTPServer audioHTTPServer, @Nullable String ipAddress,
+ BundleContext bundleContext) {
+ super(thing, audioHTTPServer, ipAddress, bundleContext);
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ }
+
+ @Override
+ void internalGetProperties(Map properties) throws FreeboxException {
+ super.internalGetProperties(properties);
+ SystemConf config = getManager(SystemManager.class).getConfig();
+ properties.put(Thing.PROPERTY_SERIAL_NUMBER, config.getSerial());
+ properties.put(Thing.PROPERTY_FIRMWARE_VERSION, config.getFirmwareVersion());
+ properties.put(Thing.PROPERTY_HARDWARE_VERSION, config.getPrettyName().orElse("Unknown"));
+ LanConfig lanConfig = getManager(LanManager.class).getConfig();
+ properties.put(NameSource.UPNP.name(), lanConfig.getName());
+ }
+
+ @Override
+ protected void internalPoll() throws FreeboxException {
+ super.internalPoll();
+ logger.debug("Polling server state...");
+ fetchConnectionStatus();
+ fetchMediaServerConfig();
+ fetchSambaConfig();
+ updateChannelOnOff(FILE_SHARING, FTP_STATUS, getManager(FtpManager.class).getStatus());
+ updateChannelOnOff(ACTIONS, WIFI_STATUS, getManager(WifiManager.class).getStatus());
+ }
+
+ @Override
+ protected ConnectivityData fetchConnectivity() throws FreeboxException {
+ return getManager(LanManager.class).getConfig();
+ }
+
+ private void fetchConnectionStatus() throws FreeboxException {
+ ConnectionStatus connectionStatus = getManager(ConnectionManager.class).getConfig();
+ QuantityType> bandwidthUp = new QuantityType<>(connectionStatus.getBandwidthUp(), BIT_PER_SECOND);
+ QuantityType> bandwidthDown = new QuantityType<>(connectionStatus.getBandwidthDown(), BIT_PER_SECOND);
+ updateChannelString(CONNECTION_STATUS, LINE_STATUS, connectionStatus.getState().name());
+ updateChannelString(CONNECTION_STATUS, IP_ADDRESS, connectionStatus.getIpv4());
+
+ QuantityType rateUp = new QuantityType<>(connectionStatus.getRateUp() * 8,
+ Units.BIT_PER_SECOND);
+ updateChannelQuantity(CONNECTION_STATUS, RATE_UP, rateUp, KILOBIT_PER_SECOND);
+ updateChannelQuantity(CONNECTION_STATUS, PCT_BW_UP, rateUp.multiply(HUNDRED).divide(bandwidthUp),
+ Units.PERCENT);
+
+ QuantityType rateDown = new QuantityType<>(connectionStatus.getRateDown() * 8,
+ Units.BIT_PER_SECOND);
+ updateChannelQuantity(CONNECTION_STATUS, RATE_DOWN, rateDown, KILOBIT_PER_SECOND);
+ updateChannelQuantity(CONNECTION_STATUS, PCT_BW_DOWN, rateDown.multiply(HUNDRED).divide(bandwidthDown),
+ Units.PERCENT);
+
+ updateChannelQuantity(CONNECTION_STATUS, BYTES_UP, new QuantityType<>(connectionStatus.getBytesUp(), OCTET),
+ GIBIOCTET);
+ updateChannelQuantity(CONNECTION_STATUS, BYTES_DOWN, new QuantityType<>(connectionStatus.getBytesDown(), OCTET),
+ GIBIOCTET);
+ }
+
+ @Override
+ protected Optional getDeviceConfig() throws FreeboxException {
+ return Optional.ofNullable(getManager(SystemManager.class).getConfig());
+ }
+
+ @Override
+ protected boolean internalHandleCommand(ChannelUID channelUID, Command command) throws FreeboxException {
+ if (ON_OFF_CLASSES.contains(command.getClass())) {
+ boolean enable = TRUE_COMMANDS.contains(command);
+ switch (channelUID.getIdWithoutGroup()) {
+ case WIFI_STATUS:
+ updateChannelOnOff(ACTIONS, WIFI_STATUS, getManager(WifiManager.class).setStatus(enable));
+ return true;
+ case FTP_STATUS:
+ updateChannelOnOff(FILE_SHARING, FTP_STATUS, getManager(FtpManager.class).setStatus(enable));
+ return true;
+ case SAMBA_FILE_STATUS:
+ updateChannelOnOff(FILE_SHARING, SAMBA_FILE_STATUS,
+ getManager(SambaManager.class).setStatus(enable));
+ return true;
+ case SAMBA_PRINTER_STATUS:
+ updateChannelOnOff(FILE_SHARING, SAMBA_PRINTER_STATUS, enableSambaPrintShare(enable));
+ return true;
+ case UPNPAV_STATUS:
+ updateChannelOnOff(ACTIONS, UPNPAV_STATUS, getManager(UPnPAVManager.class).setStatus(enable));
+ return true;
+ case AIRMEDIA_STATUS:
+ updateChannelOnOff(ACTIONS, AIRMEDIA_STATUS, getManager(AirMediaManager.class).setStatus(enable));
+ return true;
+ default:
+ break;
+ }
+ }
+ return super.internalHandleCommand(channelUID, command);
+ }
+
+ private void fetchSambaConfig() throws FreeboxException {
+ SambaConfig response = getManager(SambaManager.class).getConfig();
+ updateChannelOnOff(FILE_SHARING, SAMBA_FILE_STATUS, response.isEnabled());
+ updateChannelOnOff(FILE_SHARING, SAMBA_PRINTER_STATUS, response.isPrintShareEnabled());
+ }
+
+ private boolean enableSambaPrintShare(boolean enable) throws FreeboxException {
+ SambaConfig config = getManager(SambaManager.class).getConfig();
+ config.setPrintShareEnabled(enable);
+ config = getManager(SambaManager.class).setConfig(config);
+ return config.isPrintShareEnabled();
+ }
+
+ private void fetchMediaServerConfig() throws FreeboxException {
+ boolean airMediaStatus = false;
+ boolean uPnPAVStatus = false;
+
+ if (getManager(LanManager.class).getNetworkMode() == NetworkMode.ROUTER) {
+ airMediaStatus = getManager(AirMediaManager.class).getConfig().isEnabled();
+ uPnPAVStatus = getManager(UPnPAVManager.class).getStatus();
+ }
+
+ updateChannelOnOff(ACTIONS, AIRMEDIA_STATUS, airMediaStatus);
+ updateChannelOnOff(ACTIONS, UPNPAV_STATUS, uPnPAVStatus);
+ }
+
+ @Override
+ public Collection> getServices() {
+ return Collections.singleton(ServerActions.class);
+ }
+
+ @Override
+ protected void internalCallReboot() throws FreeboxException {
+ getManager(SystemManager.class).reboot();
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/VmHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/VmHandler.java
new file mode 100644
index 0000000000000..24b5e26eac935
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/VmHandler.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2010-2021 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 org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.vm.VirtualMachine;
+import org.openhab.binding.freeboxos.internal.api.vm.VirtualMachine.Status;
+import org.openhab.binding.freeboxos.internal.api.vm.VmManager;
+import org.openhab.binding.freeboxos.internal.config.ClientConfiguration;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.Command;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link VmHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class VmHandler extends HostHandler {
+ private final Logger logger = LoggerFactory.getLogger(VmHandler.class);
+
+ public VmHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ protected void internalPoll() throws FreeboxException {
+ super.internalPoll();
+ logger.debug("Polling Virtual machine status");
+ VmManager vmManager = getManager(VmManager.class);
+ VirtualMachine vm = vmManager.getDevice(getConfigAs(ClientConfiguration.class).id);
+ updateChannelOnOff(VM_STATUS, STATUS, vm.getStatus() == Status.RUNNING);
+ }
+
+ @Override
+ protected boolean internalHandleCommand(ChannelUID channelUID, Command command) throws FreeboxException {
+ VmManager vmManager = getManager(VmManager.class);
+ if (STATUS.equals(channelUID.getIdWithoutGroup()) && command instanceof OnOffType) {
+ vmManager.power(getConfigAs(ClientConfiguration.class).id, command == OnOffType.ON);
+ return true;
+ }
+ return super.internalHandleCommand(channelUID, command);
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/WifiHostHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/WifiHostHandler.java
new file mode 100644
index 0000000000000..d3c9ca4abbe65
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/WifiHostHandler.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2010-2021 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.util.Map;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.freeboxos.internal.api.FreeboxException;
+import org.openhab.binding.freeboxos.internal.api.lan.LanAccessPoint;
+import org.openhab.binding.freeboxos.internal.api.lan.LanHost;
+import org.openhab.binding.freeboxos.internal.api.repeater.RepeaterManager;
+import org.openhab.binding.freeboxos.internal.api.wifi.APManager;
+import org.openhab.binding.freeboxos.internal.api.wifi.AccessPointHost;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.UnDefType;
+
+/**
+ * The {@link WifiHostHandler} is responsible for handling everything associated to
+ * any Freebox thing types except the bridge thing type.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class WifiHostHandler extends HostHandler {
+ private static String SERVEUR_HOST = "Server";
+
+ public WifiHostHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ protected void internalPoll() throws FreeboxException {
+ super.internalPoll();
+ Optional host = getManager(APManager.class).getHost(getMac());
+ if (host.isPresent()) {
+ AccessPointHost wifiHost = host.get();
+ updateChannels(wifiHost.getSignal(), SERVEUR_HOST, wifiHost.getSsid());
+ } else {
+ Map map = getManager(RepeaterManager.class).getHostsMap();
+ LanHost wifiHost = map.get(getMac());
+ LanAccessPoint accessPoint = null;
+ if (wifiHost != null) {
+ accessPoint = wifiHost.getAccessPoint();
+ }
+ if (accessPoint != null) {
+ updateChannels(accessPoint.getSignal(),
+ String.format("%s-%s", accessPoint.getType(), accessPoint.getId()), accessPoint.getSsid());
+
+ } else {
+ // Not found a wifi repeater/host, so update all wifi channels to NULL
+ getThing().getChannelsOfGroup(GROUP_WIFI).stream().map(Channel::getUID).filter(uid -> isLinked(uid))
+ .forEach(uid -> updateState(uid, UnDefType.NULL));
+ }
+ }
+ }
+
+ private void updateChannels(int rssi, String host, @Nullable String ssid) {
+ updateChannelString(GROUP_WIFI, SSID, ssid);
+ updateChannelString(GROUP_WIFI, WIFI_HOST, host);
+ updateChannelQuantity(GROUP_WIFI, RSSI, rssi <= 0 ? new QuantityType<>(rssi, Units.DECIBEL_MILLIWATTS) : null);
+ updateChannelDecimal(GROUP_WIFI, WIFI_QUALITY, rssi <= 0 ? toQoS(rssi) : null);
+ }
+
+ private int toQoS(int rssi) {
+ return rssi > -50 ? 4 : rssi > -60 ? 3 : rssi > -70 ? 2 : rssi > -85 ? 1 : 0;
+ }
+}
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/binding/binding.xml
new file mode 100644
index 0000000000000..53d89b61259c8
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/binding/binding.xml
@@ -0,0 +1,9 @@
+
+
+
+ Freebox OS Binding
+ The Freebox OS binding integrates Free equipments in your home automation.
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/bridge-config.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/bridge-config.xml
new file mode 100644
index 0000000000000..99e9cbb1a798a
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/bridge-config.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+ Freebox Server Address
+ network-address
+ The domain to use in place of hardcoded Freebox ip
+ mafreebox.freebox.fr
+
+
+ Application Token
+ password
+ Token generated by the Freebox server
+
+
+ Network Device Discovery
+ Enable the discovery of network device things
+ false
+
+
+ HTTPS Available
+ Tells if https has been configured on the Freebox
+ true
+ false
+
+
+ HTTPS port
+ Port to use for remote https access to the Freebox Api
+ true
+ 15682
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/host-config.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/host-config.xml
new file mode 100644
index 0000000000000..ac3073219b5cc
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/host-config.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ Refresh Interval
+ The refresh interval in seconds which is used to poll given device
+ 30
+
+
+ MAC Address
+ The MAC address of the network device
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/landline-config.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/landline-config.xml
new file mode 100644
index 0000000000000..e62873ccd8f2b
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/landline-config.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+ State Refresh Interval
+ The refresh interval in seconds which is used to poll for phone state.
+ 2
+
+
+ ID
+ Id of the land line
+ 1
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/player-config.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/player-config.xml
new file mode 100644
index 0000000000000..d323baaa93157
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/player-config.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+ MAC Address
+ The MAC address of the player device
+
+
+ ID
+ Id of the player
+ 1
+
+
+ Player port
+ 24322
+ true
+
+
+ password
+ Password
+ AirPlay password
+ true
+
+
+ Remote Code
+ Code associated to remote control
+ true
+
+
+ Accept All MP3
+ Accept any bitrate for MP3 audio or only bitrates greater than 64 kbps
+ true
+ true
+
+
+ Refresh Interval
+ The refresh interval in seconds which is used to poll the player
+ 30
+
+
+ Callback URL
+ URL to use for playing notification sounds, e.g. 'http://192.168.0.2:8080'
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/repeater-config.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/repeater-config.xml
new file mode 100644
index 0000000000000..b78989608cca7
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/repeater-config.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Refresh Interval
+ The refresh interval in seconds which is used to poll the repeater
+ 30
+
+
+ MAC Address
+ The MAC address of the network device
+
+
+ ID
+ Id of the repeater
+ 1
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/server-config.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/server-config.xml
new file mode 100644
index 0000000000000..7bdfad57d14f9
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/server-config.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ Refresh Interval
+ The refresh interval in seconds which is used to poll given Freebox Server
+ 30
+
+
+ MAC Address
+ The MAC address of the network device
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/vm-config.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/vm-config.xml
new file mode 100644
index 0000000000000..46fad86b26af0
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/config/vm-config.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Refresh Interval
+ The refresh interval in seconds which is used to poll given virtual machine
+ 30
+
+
+ MAC Address
+ The MAC address of the network device
+
+
+ ID
+ Id of the Virtual Machine
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/api-bridge-type.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/api-bridge-type.xml
new file mode 100644
index 0000000000000..2c82d275491cb
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/api-bridge-type.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ Freebox OS Api
+ Bridge between hosts and the API rest service
+
+ api_domain
+
+
+
+
+
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
new file mode 100644
index 0000000000000..4110cd53fe329
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/channel-types.xml
@@ -0,0 +1,340 @@
+
+
+
+
+ Number:Dimensionless
+ Screen Brightness
+ Brightness level of the screen in percent
+ DimmableLight
+
+
+
+
+ Number
+ Screen Orientation
+ Screen Orientation in degrees
+
+
+ Horizontal
+ Turned left
+ Reversed
+ Turned right
+
+
+
+
+
+
+
+ String
+ SSID
+
+
+
+
+ String
+ Access Point
+
+
+
+
+ Switch
+ Forced Orientation
+ Indicates whether the screen orientation is forced
+ Switch
+
+
+
+ Switch
+ DECT Enabled
+ Activates / stops the integrated DECT base.
+ Switch
+
+
+
+ Switch
+ Alternating Ring
+ Switch
+
+
+
+ Number:Temperature
+ Temperature
+ Actual measured temperature of the sensor
+ Temperature
+
+
+
+
+ Number
+ Fan Speed
+ Actual measured rotation speed (rpm) of the sensor
+
+
+
+
+ Switch
+ Windows File Sharing Enabled
+ Indicates whether Windows File Sharing is enabled
+ Switch
+
+
+
+ Switch
+ Windows Printer Sharing Enabled
+ Indicates whether Windows Printer Sharing is enabled
+ Switch
+
+
+
+ Number:Dimensionless
+ Bandwidth Usage
+ Current bandwidth usage
+
+
+
+
+ Number:DataTransferRate
+ Transfer Rate
+ Current transfer rate
+
+
+
+
+ Number:DataAmount
+ Transfered Bytes
+ Total data transfered since last connection
+
+
+
+
+ Number:DataTransferRate
+ Bandwidth
+ Available bandwidth
+
+
+
+
+ Number:Time
+ Uptime
+ Time since last reboot of the equipment
+ time
+
+
+
+
+ String
+ Line Status
+ Status of network line connexion
+
+
+ Connection is initializing
+ Connection is active
+ Connection is about to become inactive
+ Connection is inactive
+
+
+
+
+
+ String
+ Player Status
+ Status of the Freebox TV player
+
+
+
+
+ Switch
+ Wifi Enabled
+ Indicates whether the wifi network is enabled
+ Switch
+
+
+
+ Switch
+ FTP Server Enabled
+ Indicates whether the FTP server is enabled
+ Switch
+
+
+
+ Switch
+ Air Media Enabled
+ Indicates whether Air Media is enabled
+ Switch
+
+
+
+ Switch
+ UPnP AV Enabled
+ Indicates whether UPnP AV is enabled
+ Switch
+
+
+
+ Switch
+ On Hook
+ Indicates whether the phone is on hook
+ Switch
+
+
+
+
+ Switch
+ Ringing
+ Is the phone ringing
+ Alarm
+
+
+
+ String
+ Phone Number
+
+
+
+
+ Call
+ Incoming Call
+ Details about call.
+
+
+
+
+ Number:Time
+ Duration
+ Call duration in seconds
+ time
+
+
+
+
+ DateTime
+ Timestamp
+ time
+
+
+
+
+ String
+ Name
+ Called name for outgoing calls. Caller name for incoming calls
+
+
+
+
+ Switch
+ Reachable
+ Indicates if the network device is reachable or not
+ Switch
+
+
+
+
+ Switch
+ VM Status
+
+
+
+ trigger
+ Server Event
+ Triggers when an event related to the Freebox server has been detected
+
+
+
+ trigger
+ Phone Event
+ Triggers when an event related to the phone has been detected
+
+
+
+ String
+ Remote Key Code
+ Simulates pushing a remote control button
+
+
+ Red
+ Green
+ Blue
+ Yellow
+ On/Off
+ List
+ TV
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ Volume Up
+ Volume Down
+ Mute
+ Prog Up
+ Prog Down
+ Previous
+ Backward
+ Play/Pause
+ Record
+ Forward
+ Next
+ Up
+ Right
+ Down
+ Left
+ Back
+ Swap
+ Info
+ EPG
+ Mail
+ Media
+ Help
+ Options
+ PIP
+ OK
+ Home
+
+
+
+
+
+ String
+ IP Address
+ IP address of the client
+
+
+
+
+ Switch
+ Led Activated
+ Led indicator status
+ Switch
+
+
+
+
+ String
+ Connection
+ Connection Status
+
+
+
+
+ Number
+ Host Count
+ Number of hosts connected to the device
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/host-channel-groups.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/host-channel-groups.xml
new file mode 100644
index 0000000000000..007fc0cc49392
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/host-channel-groups.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ Network Connectivity
+
+
+
+ Last Activity
+
+
+ IP Address
+ IP Address of the host.
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/host-thing-type.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/host-thing-type.xml
new file mode 100644
index 0000000000000..e1db3093ad40e
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/host-thing-type.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+ Network Device
+ Provides network device reachability
+
+
+
+
+
+ macAddress
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/landline-channel-groups.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/landline-channel-groups.xml
new file mode 100644
index 0000000000000..ac71df1c8d857
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/landline-channel-groups.xml
@@ -0,0 +1,85 @@
+
+
+
+
+ Phone
+ The phone state
+
+
+
+
+
+
+
+
+ Phone Settings
+
+
+
+
+
+
+
+ Accepted Call
+ The last accepted phone call
+
+
+ Calling Number
+ Caller phone number
+
+
+ Incoming Call Duration
+
+
+ Incoming Call Timestamp
+
+
+ Accepted Caller
+ Caller name
+
+
+
+
+
+ Missed Call
+ The last missed phone call
+
+
+ Missed Calling Number
+ Caller phone number
+
+
+ Missed Call Timestamp
+
+
+ Missed Caller
+ Caller name
+
+
+
+
+
+ Outgoing Call
+ The last outgoing phone call
+
+
+ Called Number
+ Called phone number
+
+
+ Outgoing Call Duration
+
+
+ Outgoing Call Timestamp
+
+
+ Called Name
+ Name, if known, of the called person
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/landline-thing-type.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/landline-thing-type.xml
new file mode 100644
index 0000000000000..11fe64a8bb851
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/landline-thing-type.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+ Landline
+ Provides various informations regarding the landline state and the phone calls
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/player-channel-groups.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/player-channel-groups.xml
new file mode 100644
index 0000000000000..44b88f91b432a
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/player-channel-groups.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ Player Actions
+
+
+
+
+
+
+ Player Status
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/player-thing-type.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/player-thing-type.xml
new file mode 100644
index 0000000000000..199fdbd47787a
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/player-thing-type.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ Freebox Player
+ The player is the device connected to your TV
+
+
+
+
+
+
+
+ macAddress
+
+
+
+
+
+
+
+
+
+ Freebox Player
+ The player is the device connected to your TV with API capabilities
+
+
+
+
+
+
+
+
+ macAddress
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/repeater-channel-groups.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/repeater-channel-groups.xml
new file mode 100644
index 0000000000000..96b023d5cdcba
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/repeater-channel-groups.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ Repeater Settings
+
+
+ Started Since
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/repeater-thing-type.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/repeater-thing-type.xml
new file mode 100644
index 0000000000000..326c98ba4fea9
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/repeater-thing-type.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+ Wifi Repeater
+ Provides informations and control over a Wifi Repeater
+
+
+
+
+
+
+ macAddress
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/server-channel-groups.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/server-channel-groups.xml
new file mode 100644
index 0000000000000..efe448e724a7a
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/server-channel-groups.xml
@@ -0,0 +1,81 @@
+
+
+
+
+ System Sensors
+
+
+
+ System Informations
+
+
+
+
+
+
+
+ Server Settings
+
+
+
+
+
+
+
+
+ File Sharing
+
+
+
+
+
+
+
+
+ Front Display Panel
+
+
+
+
+
+
+
+
+ Connection Status Details
+
+
+
+ Public IP
+ Public IP Address of the Freebox Server
+
+
+ Bandwidth Usage Up
+ Portion of the upload bandwidth currently used
+
+
+ Bandwidth Usage Down
+ Portion of the download bandwidth currently used
+
+
+ Upload Rate
+ Current upload rate
+
+
+ Download Rate
+ Current download rate
+
+
+ Uploaded
+ Total data uploaded since last restart
+
+
+ Downloaded
+ Total data downloaded since last restart
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/server-thing-type.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/server-thing-type.xml
new file mode 100644
index 0000000000000..00b456f886a4b
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/server-thing-type.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+ Freebox Revolution
+ Provides various informations regarding the status of the Freebox Revolution Server
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Freebox Delta
+ Provides various informations regarding the status of the Freebox Delta Server
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/vm-channel-groups.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/vm-channel-groups.xml
new file mode 100644
index 0000000000000..6c3092cd1bcf5
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/vm-channel-groups.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ VM Status
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/vm-thing-type.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/vm-thing-type.xml
new file mode 100644
index 0000000000000..ac5a742e770d7
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/vm-thing-type.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+ Virtual Machine
+ Provides informations and control over virtual machine hosted on the server
+
+
+
+
+
+
+ macAddress
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/wifi-channel-groups.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/wifi-channel-groups.xml
new file mode 100644
index 0000000000000..71307d1bb7dc5
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/wifi-channel-groups.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ Wifi Related Information
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/wifihost-thing-type.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/wifihost-thing-type.xml
new file mode 100644
index 0000000000000..581ba2c50cf72
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/wifihost-thing-type.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+ Wifi Device
+ Provides Wifi device reachability
+
+
+
+
+
+
+ macAddress
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/freeboxECCRootCA.crt b/bundles/org.openhab.binding.freeboxos/src/main/resources/freeboxECCRootCA.crt
new file mode 100644
index 0000000000000..eab91f680f811
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/freeboxECCRootCA.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWTCCAd+gAwIBAgIJAMaRcLnIgyukMAoGCCqGSM49BAMCMGExCzAJBgNVBAYT
+AkZSMQ8wDQYDVQQIDAZGcmFuY2UxDjAMBgNVBAcMBVBhcmlzMRMwEQYDVQQKDApG
+cmVlYm94IFNBMRwwGgYDVQQDDBNGcmVlYm94IEVDQyBSb290IENBMB4XDTE1MDkw
+MTE4MDIwN1oXDTM1MDgyNzE4MDIwN1owYTELMAkGA1UEBhMCRlIxDzANBgNVBAgM
+BkZyYW5jZTEOMAwGA1UEBwwFUGFyaXMxEzARBgNVBAoMCkZyZWVib3ggU0ExHDAa
+BgNVBAMME0ZyZWVib3ggRUNDIFJvb3QgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
+AASCjD6ZKn5ko6cU5Vxh8GA1KqRi6p2GQzndxHtuUmwY8RvBbhZ0GIL7bQ4f08ae
+JOv0ycWjEW0fyOnAw6AYdsN6y1eNvH2DVfoXQyGoCSvXQNAUxla+sJuLGICRYiZz
+mnijYzBhMB0GA1UdDgQWBBTIB3c2GlbV6EIh2ErEMJvFxMz/QTAfBgNVHSMEGDAW
+gBTIB3c2GlbV6EIh2ErEMJvFxMz/QTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
+/wQEAwIBhjAKBggqhkjOPQQDAgNoADBlAjA8tzEMRVX8vrFuOGDhvZr7OSJjbBr8
+gl2I70LeVNGEXZsAThUkqj5Rg9bV8xw3aSMCMQCDjB5CgsLH8EdZmiksdBRRKM2r
+vxo6c0dSSNrr7dDN+m2/dRvgoIpGL2GauOGqDFY=
+-----END CERTIFICATE-----
diff --git a/bundles/org.openhab.binding.freeboxos/src/test/java/org/openhab/binding/freeboxos/internal/api/model/OpenSessionDataTest.java b/bundles/org.openhab.binding.freeboxos/src/test/java/org/openhab/binding/freeboxos/internal/api/model/OpenSessionDataTest.java
new file mode 100644
index 0000000000000..6579d41bdfeea
--- /dev/null
+++ b/bundles/org.openhab.binding.freeboxos/src/test/java/org/openhab/binding/freeboxos/internal/api/model/OpenSessionDataTest.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2021 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.api.model;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.freeboxos.internal.api.login.OpenSessionData;
+
+/**
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class OpenSessionDataTest {
+
+ @Test
+ public void hmacSha1Test() throws Exception {
+ String expected = "25dad1bb5604321f12b755cc9d755d1480cf7989";
+ OpenSessionData openSessionData = new OpenSessionData("foo", "Token1234", "Challenge");
+ String actual = openSessionData.getPassword();
+ assertEquals(expected, actual);
+ }
+}
diff --git a/bundles/pom.xml b/bundles/pom.xml
index 24e1feb55f47b..aba074e683acc 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -123,6 +123,7 @@
org.openhab.binding.folding
org.openhab.binding.foobot
org.openhab.binding.freebox
+ org.openhab.binding.freeboxos
org.openhab.binding.fronius
org.openhab.binding.fsinternetradio
org.openhab.binding.ftpupload