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 @@ + + + + + + + network-address + The domain to use in place of hardcoded Freebox ip + mafreebox.freebox.fr + + + + password + Token generated by the Freebox server + + + + Enable the discovery of network device things + false + + + + Tells if https has been configured on the Freebox + true + false + + + + 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 @@ + + + + + + + The refresh interval in seconds which is used to poll given device + 30 + + + + 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 @@ + + + + + + + The refresh interval in seconds which is used to poll for phone state. + 2 + + + + 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 @@ + + + + + + + The MAC address of the player device + + + + Id of the player + 1 + + + + 24322 + true + + + password + + AirPlay password + true + + + + Code associated to remote control + true + + + + Accept any bitrate for MP3 audio or only bitrates greater than 64 kbps + true + true + + + + The refresh interval in seconds which is used to poll the player + 30 + + + + 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 @@ + + + + + + + The refresh interval in seconds which is used to poll the repeater + 30 + + + + The MAC address of the network device + + + + 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 @@ + + + + + + + The refresh interval in seconds which is used to poll given Freebox Server + 30 + + + + 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 @@ + + + + + + + The refresh interval in seconds which is used to poll given virtual machine + 30 + + + + The MAC address of the network device + + + + 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 @@ + + + + + + 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 + + Brightness level of the screen in percent + DimmableLight + + + + + Number + + Screen Orientation in degrees + + + + + + + + + + + + Number:Power + + Received signal strength indicator + QualityOfService + + + + + String + + + + + + String + + + + + + Switch + + Indicates whether the screen orientation is forced + Switch + + + + Switch + + Activates / stops the integrated DECT base. + Switch + + + + Switch + + Switch + + + + Number:Temperature + + Actual measured temperature of the sensor + Temperature + + + + + Number + + Actual measured rotation speed (rpm) of the sensor + + + + + Switch + + Indicates whether Windows File Sharing is enabled + Switch + + + + Switch + + Indicates whether Windows Printer Sharing is enabled + Switch + + + + Number:Dimensionless + + Current bandwidth usage + + + + + Number:DataTransferRate + + Current transfer rate + + + + + Number:DataAmount + + Total data transfered since last connection + + + + + Number:DataTransferRate + + Available bandwidth + + + + + Number:Time + + Time since last reboot of the equipment + time + + + + + String + + Status of network line connexion + + + + + + + + + + + + String + + Status of the Freebox TV player + + + + + Switch + + Indicates whether the wifi network is enabled + Switch + + + + Switch + + Indicates whether the FTP server is enabled + Switch + + + + Switch + + Indicates whether Air Media is enabled + Switch + + + + Switch + + Indicates whether UPnP AV is enabled + Switch + + + + Switch + + Indicates whether the phone is on hook + Switch + + + + + Switch + + Is the phone ringing + Alarm + + + + String + + + + + + Call + + Details about call. + + + + + Number:Time + + Call duration in seconds + time + + + + + DateTime + + time + + + + + String + + Called name for outgoing calls. Caller name for incoming calls + + + + + Switch + + Indicates if the network device is reachable or not + Switch + + + + + Switch + + + + + trigger + + Triggers when an event related to the Freebox server has been detected + + + + trigger + + Triggers when an event related to the phone has been detected + + + + String + + Simulates pushing a remote control button + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String + + IP address of the client + + + + + Switch + + Led indicator status + Switch + + + + + String + + Connection Status + + + + + Number + + 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 @@ + + + + + + + + + + + + + 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 @@ + + + + + + + + + + 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 @@ + + + + + + The phone state + + + + + + + + + + + + + + + + + + The last accepted phone call + + + + Caller phone number + + + + + + + + + + Caller name + + + + + + + The last missed phone call + + + + Caller phone number + + + + + + + Caller name + + + + + + + The last outgoing phone call + + + + Called phone number + + + + + + + + + + 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 @@ + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + The player is the device connected to your TV + + + + + + + + macAddress + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Public IP Address of the Freebox Server + + + + Portion of the upload bandwidth currently used + + + + Portion of the download bandwidth currently used + + + + Current upload rate + + + + Current download rate + + + + Total data uploaded since last restart + + + + 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 @@ + + + + + + + + + + Provides various informations regarding the status of the Freebox Revolution Server + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + 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 @@ + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + 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