added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.freebox</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -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

View File

@@ -0,0 +1,240 @@
# 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' Bridge 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 |
|---------------|------------|---------------------------------------------------------|
| server | Bridge | The Freebox Server. |
| phone | Thing | The phone wired to the Freebox Server. |
| net_device | Thing | A network device on the local network. |
| net_interface | Thing | A network interface from a device on the local network. |
| airplay | Thing | An AirPlay device in the local network. |
## Discovery
The Freebox Server is discovered automatically through mDNS in the local network.
After a Freebox Server is discovered and available to openHAB, the binding will automatically discover phone, network devices / interfaces and AirPlay devices with video capability in 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 has the following configuration options, which can be set for "binding:freebox":
| Parameter | Name | Description | Required |
|-------------|--------------|----------------------------------------------------------------------------|----------|
| callbackUrl | Callback URL | URL to use for playing notification sounds, e.g. <http://192.168.0.2:8080> | no |
## Thing Configuration
### Server
The *server* bridge thing requires the following configuration parameters:
| Parameter Label | Parameter ID | Description | Required | Default |
|------------------------------------|-------------------------|-----------------------------------------------------------------------------|----------|----------------------|
| Freebox Network Address | fqdn | The IP address / FQDN of the Freebox Server (can include port number). | false | mafreebox.freebox.fr |
| Application token | appToken | Token generated by the Freebox Server. | false | |
| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll given Freebox Server. | false | 30 |
| Use only HTTP API | useOnlyHttp | Use HTTP API even if HTTPS is available. | false | false |
| Enable Phone Discovery | discoverPhone | Enable the discovery of phone things. | false | true |
| Enable Network Device Discovery | discoverNetDevice | Enable the discovery of network device things. | false | true |
| Enable Network Interface Discovery | discoverNetInterface | Enable the discovery of network interface things. | false | true |
| Enable AirPlay Receiver Discovery | discoverAirPlayReceiver | Enable the discovery of AirPlay receiver things. | false | true |
If the parameter *fqdn* 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.
### Phone
The *phone* thing requires the following configuration parameters:
| Parameter Label | Parameter ID | Description | Required | Default |
|------------------------------|---------------------------|---------------------------------------------------------------------------------------------|----------|---------|
| Phone State Refresh Interval | refreshPhoneInterval | The refresh interval in seconds which is used to poll given Freebox Server for phone state. | false | 2 |
| Phone Calls Refresh Interval | refreshPhoneCallsInterval | The refresh interval in seconds which is used to poll given Freebox Server for phone calls. | false | 60 |
### Network device
The *net_device* thing requires the following configuration parameters:
| Parameter Label | Parameter ID | Description | Required |
|-----------------|--------------|----------------------------------------|----------|
| MAC Address | macAddress | The MAC address of the network device. | true |
### Network interface
The *net_interface* thing requires the following configuration parameters:
| Parameter Label | Parameter ID | Description | Required |
|-----------------|--------------|-----------------------------------------------------|----------|
| IP Address | ipAddress | The IP address (v4 or v6) of the network interface. | true |
### AirPlay device
The *airplay* thing requires the following configuration parameters:
| Parameter Label | Parameter ID | Description | Required |
|-----------------|--------------|-----------------------------|----------|
| Name | name | Name of the AirPlay device | true |
| Password | password | AirPlay password | false |
| Accept all MP3 | acceptAllMp3 | Accept any bitrate for MP3 audio or only bitrates greater than 64 kbps | false |
## 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 `freebox <bridgeUID> 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 |
|---------------|--------------------------|-----------|-------------|---------------------------------------------------------------------------------|
| server | fwversion | String | R | Version of the Freebox Server firmware |
| server | uptime | Number | R | Time since last reboot of the Freebox Server |
| server | restarted | Switch | R | Indicates whether the Freebox server hase restarted during the last poll period |
| server | tempcpum | Number | R | Actual measured CPU Marvell temperature |
| server | tempcpub | Number | R | Actual measured CPU Broadcom (xDSL) temperature |
| server | tempswitch | Number | R | Actual measured switch temperature |
| server | fanspeed | Number | R | Actual measured fan speed (rpm) |
| server | reboot | Switch | W | Reboots the Freebox server |
| server | lcd_brightness | Number | RW | Brightness level of the screen in percent |
| server | lcd_orientation | Number | RW | Screen Orientation in degrees (0 or 90 or 180 or 270) |
| server | lcd_forced | Switch | RW | Indicates whether the screen orientation forced |
| 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 | sambafileshare_status | Switch | RW | Indicates whether Window File Sharing is enabled |
| server | sambaprintershare_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 | any#call_number | String | R | Last call: number |
| phone | any#call_duration | Number | R | Last call: duration in seconds |
| phone | any#call_timestamp | DateTime | R | Last call: creation timestamp |
| phone | any#call_status | String | R | Last call: type (ingoing, outgoing, missed) |
| phone | any#call_name | String | R | Last call: called name for outgoing calls. Caller name for incoming calls |
| phone | accepted#call_number | String | R | Last accepted call: number |
| phone | accepted#call_duration | Number | R | Last accepted call: duration in seconds |
| phone | accepted#call_timestamp | DateTime | R | Last accepted call: creation timestamp |
| phone | accepted#call_name | String | R | Last accepted call: caller name |
| phone | missed#call_number | String | R | Last missed call: number |
| phone | missed#call_duration | Number | R | Last missed call: duration in seconds |
| phone | missed#call_timestamp | DateTime | R | Last missed call: creation timestamp |
| phone | missed#call_name | String | R | Last missed call: caller name |
| phone | outgoing#call_number | String | R | Last outgoing call: number |
| phone | outgoing#call_duration | Number | R | Last outgoing call: duration in seconds |
| phone | outgoing#call_timestamp | DateTime | R | Last outgoing call: creation timestamp |
| phone | outgoing#call_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 |
## Example
### Things
Here is an example with minimal configuration parameters (using default values).
It will first connect to mafreebox.freebox.fr using HTTPS (and will failback to HTTP if HTTPS access is not available).
```java
Bridge freebox:server:fb "Freebox Revolution" [ appToken="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ] {
Thing phone Phone "Phone"
Thing net_device tv1 "TV living room" [ macAddress="XX:XX:XX:XX:XX:XX" ]
Thing net_interface tv2 "TV bedroom" [ ipAddress="192.168.0.100" ]
Thing airplay player "Freebox Player (AirPlay)" [ name="Freebox Player" ]
}
```
Here is another example overwritting default configuration parameters:
```java
Bridge freebox:server:fb "Freebox Revolution" [ fqdn="abcdefgh.fbxos.fr", appToken="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", refreshInterval=20, useOnlyHttp=false, discoverPhone=false, discoverNetDevice=false, discoverNetInterface=false, discoverAirPlayReceiver=false ] {
Thing phone Phone "Phone" [ refreshPhoneInterval=10, refreshPhoneCallsInterval=120 ]
Thing net_device tv1 "TV living room" [ macAddress="XX:XX:XX:XX:XX:XX" ]
Thing net_interface tv2 "TV bedroom" [ ipAddress="192.168.0.100" ]
Thing airplay player "Freebox Player (AirPlay)" [ name="Freebox Player", password="1111", acceptAllMp3=false ]
}
```
### Items
```java
String Freebox_xdsl_status "Freebox xDSL state [%s]" {channel="freebox:server:fb:xdsl_status"}
Switch Freebox_ftth_status "Freebox Fiber Optic state" {channel="freebox:server:fb:ftth_status"}
String Freebox_cs_state "State [%s]" {channel="freebox:server:fb:line_status"}
String Freebox_cs_ipv4 "ipV4 [%s]" {channel="freebox:server:fb:ipv4"}
Number Freebox_cs_rate_up "Upload rate [%d b/s]" {channel="freebox:server:fb:rate_up"}
Number Freebox_cs_rate_down "Download rate [%d b/s]" {channel="freebox:server:fb:rate_down"}
Number Freebox_cs_bytes_up "Uploaded [%d bytes]" {channel="freebox:server:fb:bytes_up"}
Number Freebox_cs_bytes_down "Downloaded [%d bytes]" {channel="freebox:server:fb:bytes_down"}
String Freebox_sys_firmware_version "Version [%s]" {channel="freebox:server:fb:fwversion"}
Switch Freebox_reboot "Reboot freebox" {channel="freebox:server:fb:reboot"}
Switch Freebox_restarted "Restarted" {channel="freebox:server:fb:restarted"}
Number Freebox_sys_uptime "Uptime [%d sec]" {channel="freebox:server:fb:uptime"}
Number Freebox_sys_temp_cpum "Temp cpum [%d °C]" <temperature> {channel="freebox:server:fb:tempcpum"}
Number Freebox_sys_temp_cpub "Temp cpub [%d °C]" <temperature> {channel="freebox:server:fb:tempcpub"}
Number Freebox_sys_temp_sw "Temp sw [%d °C]" <temperature> {channel="freebox:server:fb:tempswitch"}
Number Freebox_sys_fan_rpm "Fan [%d rpm]" <fan> {channel="freebox:server:fb:fanspeed"}
Switch FreeboWifi "Wifi" {channel="freebox:server:fb:wifi_status"}
Switch FreeboxFTP "FTP" {channel="freebox:server:fb:ftp_status"}
Switch FreeboxUPnPAV "UPnP AV" {channel="freebox:server:fb:upnpav_status"}
Switch FreeboxAirMedia "AirMedia" {channel="freebox:server:fb:airmedia_status"}
Switch FreeboxSambaFiles "Win File Share" {channel="freebox:server:fb:sambafileshare_status"}
Switch FreeboxSambaPrinters "Win Print Share" {channel="freebox:server:fb:sambaprintershare_status"}
Number Freebox_lcd_brightness "Brightness [%d %%]" {channel="freebox:server:fb:lcd_brightness"}
Number Freebox_lcd_orientation "Orientation [%d °]" {channel="freebox:server:fb:lcd_orientation"}
Switch Freebox_lcd_forced "LCD Forced" {channel="freebox:server:fb:lcd_forced"}
Switch Freebox_onhook "Phone on hook" {channel="freebox:phone:fb:Phone:state#onhook"}
Switch Freebox_ringing "Phone ringing" {channel="freebox:phone:fb:Phone:state#ringing"}
String Freebox_call_number "Last call [%s]" {channel="freebox:phone:fb:Phone:any#call_number"}
String Freebox_call_name "Name [%s]" {channel="freebox:phone:fb:Phone:any#call_name"}
Number Freebox_call_duration "Duration [%d sec]" {channel="freebox:phone:fb:Phone:any#call_duration"}
DateTime Freebox_call_ts "TimeStamp [%1$tA %1$td %1$tR]" <calendar> {channel="freebox:phone:fb:Phone:any#call_timestamp"}
String Freebox_call_status "Status [%s]" {channel="freebox:phone:fb:Phone:any#call_status"}
String Freebox_accepted_call_number "Last accepted call [%s]" {channel="freebox:phone:fb:Phone:accepted#call_number"}
String Freebox_accepted_call_name "Caller name [%s]" {channel="freebox:phone:fb:Phone:accepted#call_name"}
Number Freebox_accepted_call_duration "Duration [%d sec]" {channel="freebox:phone:fb:Phone:accepted#call_duration"}
DateTime Freebox_accepted_call_ts "TimeStamp [%1$tA %1$td %1$tR]" <calendar> {channel="freebox:phone:fb:Phone:accepted#call_timestamp"}
String Freebox_missed_call_lastnum "Last missed call [%s]" {channel="freebox:phone:fb:Phone:missed#call_number"}
String Freebox_missed_call_name "Caller name [%s]" {channel="freebox:phone:fb:Phone:missed#call_name"}
Number Freebox_missed_call_duration "Duration [%d sec]" {channel="freebox:phone:fb:Phone:missed#call_duration"}
DateTime Freebox_missed_call_ts "TimeStamp [%1$tA %1$td %1$tR]" <calendar> {hannel="freebox:phone:fb:Phone:missed#call_timestamp"}
String Freebox_outcall_lastnum "Last outgoing call [%s]" {channel="freebox:phone:fb:Phone:outgoing#call_number"}
String Freebox_outcall_name "Called name [%s]" {channel="freebox:phone:fb:Phone:outgoing#call_name"}
Number Freebox_outcall_duration "Duration [%d sec]" {channel="freebox:phone:fb:Phone:outgoing#call_duration"}
DateTime Freebox_outcall_ts "TimeStamp [%1$tA %1$td %1$tR]" <calendar> {channel="freebox:phone:fb:Phone:outgoing#call_timestamp"}
Switch TVLivingRoom "TV living room" <television> {channel="freebox:net_device:fb:tv1:reachable"}
Switch TVBedroom "TV bedroom" <television> {channel="freebox:net_interface:fb:tv2:reachable"}
String freebox_player_playurl "URL [%s]" { channel="freebox:airplay:fb:player:playurl" }
Switch freebox_player_stop "Stop playback" { channel="freebox:airplay:fb:player:stop" }
```

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.freebox</artifactId>
<name>openHAB Add-ons :: Bundles :: Freebox Binding</name>
</project>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.freebox-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-freebox" description="Freebox Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-mdns</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.freebox/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,176 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal;
import static org.openhab.core.audio.AudioFormat.*;
import java.io.IOException;
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.freebox.internal.api.FreeboxException;
import org.openhab.binding.freebox.internal.config.FreeboxAirPlayDeviceConfiguration;
import org.openhab.binding.freebox.internal.handler.FreeboxThingHandler;
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.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.util.ThingHandlerHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This makes an AirPlay device to serve as an {@link AudioSink}-
*
* @author Laurent Garnier - Initial contribution for AudioSink and notifications
*/
@NonNullByDefault
public class FreeboxAirPlayAudioSink implements AudioSink {
private final Logger logger = LoggerFactory.getLogger(FreeboxAirPlayAudioSink.class);
private static final AudioFormat MP3_96 = new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 96000, null);
private static final AudioFormat MP3_112 = new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 112000, null);
private static final AudioFormat MP3_128 = new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 128000, null);
private static final AudioFormat MP3_160 = new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 160000, null);
private static final AudioFormat MP3_192 = new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 192000, null);
private static final AudioFormat MP3_224 = new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 224000, null);
private static final AudioFormat MP3_256 = new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 256000, null);
private static final AudioFormat MP3_320 = new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 320000, null);
private static final Set<AudioFormat> SUPPORTED_FORMATS = new HashSet<>();
private static final HashSet<Class<? extends AudioStream>> SUPPORTED_STREAMS = new HashSet<>();
private AudioHTTPServer audioHTTPServer;
private FreeboxThingHandler handler;
private @Nullable String callbackUrl;
static {
SUPPORTED_STREAMS.add(AudioStream.class);
}
public FreeboxAirPlayAudioSink(FreeboxThingHandler handler, AudioHTTPServer audioHTTPServer,
@Nullable String callbackUrl) {
this.handler = handler;
this.audioHTTPServer = audioHTTPServer;
this.callbackUrl = callbackUrl;
Boolean acceptLowBitrate = (Boolean) handler.getThing().getConfiguration()
.get(FreeboxAirPlayDeviceConfiguration.ACCEPT_ALL_MP3);
SUPPORTED_FORMATS.add(WAV);
if (acceptLowBitrate) {
SUPPORTED_FORMATS.add(MP3);
} else {
// Only accept MP3 bitrates >= 96 kbps
SUPPORTED_FORMATS.add(MP3_96);
SUPPORTED_FORMATS.add(MP3_112);
SUPPORTED_FORMATS.add(MP3_128);
SUPPORTED_FORMATS.add(MP3_160);
SUPPORTED_FORMATS.add(MP3_192);
SUPPORTED_FORMATS.add(MP3_224);
SUPPORTED_FORMATS.add(MP3_256);
SUPPORTED_FORMATS.add(MP3_320);
}
SUPPORTED_FORMATS.add(OGG);
}
@Override
public String getId() {
return handler.getThing().getUID().toString();
}
@Override
public @Nullable String getLabel(@Nullable Locale locale) {
return handler.getThing().getLabel();
}
@Override
public void process(@Nullable AudioStream audioStream)
throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
if (!ThingHandlerHelper.isHandlerInitialized(handler)
|| ((handler.getThing().getStatus() == ThingStatus.OFFLINE)
&& ((handler.getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_OFFLINE)
|| (handler.getThing().getStatusInfo()
.getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR)))) {
return;
}
if (audioStream == null) {
try {
handler.stopMedia();
} catch (FreeboxException e) {
logger.warn("Exception while stopping audio stream playback: {}", e.getMessage());
}
return;
}
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 {
if (callbackUrl != null) {
// we serve it on our own HTTP server
String relativeUrl;
if (audioStream instanceof FixedLengthAudioStream) {
relativeUrl = audioHTTPServer.serve((FixedLengthAudioStream) audioStream, 20);
} else {
relativeUrl = audioHTTPServer.serve(audioStream);
}
url = callbackUrl + relativeUrl;
} else {
logger.warn("We do not have any callback url, so AirPlay device cannot play the audio stream!");
}
}
try {
audioStream.close();
} catch (IOException e) {
logger.debug("Exception while closing audioStream", e);
}
try {
logger.debug("AirPlay audio sink: process url {}", url);
handler.playMedia(url);
} catch (FreeboxException e) {
logger.warn("Audio stream playback failed: {}", e.getMessage());
}
}
@Override
public Set<AudioFormat> getSupportedFormats() {
return SUPPORTED_FORMATS;
}
@Override
public Set<Class<? extends AudioStream>> getSupportedStreams() {
return SUPPORTED_STREAMS;
}
@Override
public PercentType getVolume() {
throw new UnsupportedOperationException("Volume can not be determined");
}
@Override
public void setVolume(PercentType volume) {
throw new UnsupportedOperationException("Volume can not be set");
}
}

View File

@@ -0,0 +1,99 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link FreeboxBinding} class defines common constants, which are
* used across the whole binding.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class FreeboxBindingConstants {
public static final String BINDING_ID = "freebox";
// List of all Bridge Type UIDs
public static final ThingTypeUID FREEBOX_BRIDGE_TYPE_SERVER = new ThingTypeUID(BINDING_ID, "server");
// List of all Thing Type UIDs
public static final ThingTypeUID FREEBOX_THING_TYPE_PHONE = new ThingTypeUID(BINDING_ID, "phone");
public static final ThingTypeUID FREEBOX_THING_TYPE_NET_DEVICE = new ThingTypeUID(BINDING_ID, "net_device");
public static final ThingTypeUID FREEBOX_THING_TYPE_NET_INTERFACE = new ThingTypeUID(BINDING_ID, "net_interface");
public static final ThingTypeUID FREEBOX_THING_TYPE_AIRPLAY = new ThingTypeUID(BINDING_ID, "airplay");
// All supported Bridge types
public static final Set<ThingTypeUID> SUPPORTED_BRIDGE_TYPES_UIDS = Collections
.singleton(FREEBOX_BRIDGE_TYPE_SERVER);
// All supported Thing types
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.of(FREEBOX_THING_TYPE_PHONE, FREEBOX_THING_TYPE_NET_DEVICE,
FREEBOX_THING_TYPE_NET_INTERFACE, FREEBOX_THING_TYPE_AIRPLAY).collect(Collectors.toSet()));
// List of properties
public static final String API_BASE_URL = "apiBaseUrl";
public static final String API_VERSION = "apiVersion";
// List of all Group Channel ids
public static final String STATE = "state";
public static final String ANY = "any";
public static final String ACCEPTED = "accepted";
public static final String MISSED = "missed";
public static final String OUTGOING = "outgoing";
// List of all Channel ids
public static final String FWVERSION = "fwversion";
public static final String UPTIME = "uptime";
public static final String RESTARTED = "restarted";
public static final String TEMPCPUM = "tempcpum";
public static final String TEMPCPUB = "tempcpub";
public static final String TEMPSWITCH = "tempswitch";
public static final String FANSPEED = "fanspeed";
public static final String LCDBRIGHTNESS = "lcd_brightness";
public static final String LCDORIENTATION = "lcd_orientation";
public static final String LCDFORCED = "lcd_forced";
public static final String WIFISTATUS = "wifi_status";
public static final String XDSLSTATUS = "xdsl_status";
public static final String FTTHSTATUS = "ftth_status";
public static final String LINESTATUS = "line_status";
public static final String IPV4 = "ipv4";
public static final String RATEUP = "rate_up";
public static final String RATEDOWN = "rate_down";
public static final String BYTESUP = "bytes_up";
public static final String BYTESDOWN = "bytes_down";
public static final String ONHOOK = "onhook";
public static final String RINGING = "ringing";
public static final String CALLNUMBER = "call_number";
public static final String CALLDURATION = "call_duration";
public static final String CALLTIMESTAMP = "call_timestamp";
public static final String CALLSTATUS = "call_status";
public static final String CALLNAME = "call_name";
public static final String REBOOT = "reboot";
public static final String FTPSTATUS = "ftp_status";
public static final String AIRMEDIASTATUS = "airmedia_status";
public static final String UPNPAVSTATUS = "upnpav_status";
public static final String SAMBAFILESTATUS = "sambafileshare_status";
public static final String SAMBAPRINTERSTATUS = "sambaprintershare_status";
public static final String REACHABLE = "reachable";
public static final String PLAYURL = "playurl";
public static final String STOP = "stop";
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal;
import java.util.List;
import java.util.Map;
import org.openhab.binding.freebox.internal.api.model.FreeboxAirMediaReceiver;
import org.openhab.binding.freebox.internal.api.model.FreeboxLanHost;
import org.openhab.core.thing.ThingUID;
/**
* The {@link FreeboxDataListener} is notified by the bridge thing handler
* with updated data from the Freebox server.
*
* @author Laurent Garnier - Initial contribution
* @author Laurent Garnier - add discovery configuration
* @author Laurent Garnier - use new internal classes
*/
public interface FreeboxDataListener {
/**
* Update the discovery configuration.
*
* @param configProperties the configuration
*/
public void applyConfig(Map<String, Object> configProperties);
/**
* This method is called just after the bridge thing handler fetched new data
* from the Freebox server.
*
* @param bridge the Freebox server bridge.
* @param lanHosts the LAN data received from the Freebox server.
* @param airPlayDevices the list of AirPlay devices received from the Freebox server.
*/
public void onDataFetched(ThingUID bridge, List<FreeboxLanHost> lanHosts,
List<FreeboxAirMediaReceiver> airPlayDevices);
}

View File

@@ -0,0 +1,202 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
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.freebox.internal.discovery.FreeboxDiscoveryService;
import org.openhab.binding.freebox.internal.handler.FreeboxHandler;
import org.openhab.binding.freebox.internal.handler.FreeboxThingHandler;
import org.openhab.core.audio.AudioHTTPServer;
import org.openhab.core.audio.AudioSink;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.net.HttpServiceUtil;
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.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.framework.ServiceRegistration;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link FreeboxHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Gaël L'hopital - Initial contribution
* @author Laurent Garnier - several thing types and handlers + discovery service
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.freebox")
public class FreeboxHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
.concat(FreeboxBindingConstants.SUPPORTED_BRIDGE_TYPES_UIDS.stream(),
FreeboxBindingConstants.SUPPORTED_THING_TYPES_UIDS.stream())
.collect(Collectors.toSet());
private final Logger logger = LoggerFactory.getLogger(FreeboxHandlerFactory.class);
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
private final Map<ThingUID, ServiceRegistration<AudioSink>> audioSinkRegistrations = new ConcurrentHashMap<>();
private final AudioHTTPServer audioHTTPServer;
private final NetworkAddressService networkAddressService;
private final TimeZoneProvider timeZoneProvider;
// url (scheme+server+port) to use for playing notification sounds
private @Nullable String callbackUrl;
@Activate
public FreeboxHandlerFactory(final @Reference AudioHTTPServer audioHTTPServer,
final @Reference NetworkAddressService networkAddressService,
final @Reference TimeZoneProvider timeZoneProvider) {
this.audioHTTPServer = audioHTTPServer;
this.networkAddressService = networkAddressService;
this.timeZoneProvider = timeZoneProvider;
}
@Override
protected void activate(ComponentContext componentContext) {
super.activate(componentContext);
Dictionary<String, Object> properties = componentContext.getProperties();
callbackUrl = (String) properties.get("callbackUrl");
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
public @Nullable Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration,
@Nullable ThingUID thingUID, @Nullable ThingUID bridgeUID) {
if (thingTypeUID.equals(FreeboxBindingConstants.FREEBOX_BRIDGE_TYPE_SERVER)) {
return super.createThing(thingTypeUID, configuration, thingUID, null);
} else if (FreeboxBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
ThingUID newThingUID;
if (bridgeUID != null && thingUID != null) {
newThingUID = new ThingUID(thingTypeUID, bridgeUID, thingUID.getId());
} else {
newThingUID = thingUID;
}
return super.createThing(thingTypeUID, configuration, newThingUID, bridgeUID);
}
throw new IllegalArgumentException(
"The thing type " + thingTypeUID + " is not supported by the Freebox binding.");
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(FreeboxBindingConstants.FREEBOX_BRIDGE_TYPE_SERVER)) {
FreeboxHandler handler = new FreeboxHandler((Bridge) thing);
registerDiscoveryService(handler);
return handler;
} else if (FreeboxBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
FreeboxThingHandler handler = new FreeboxThingHandler(thing, timeZoneProvider);
if (FreeboxBindingConstants.FREEBOX_THING_TYPE_AIRPLAY.equals(thingTypeUID)) {
registerAudioSink(handler);
}
return handler;
}
return null;
}
@Override
protected void removeHandler(ThingHandler thingHandler) {
if (thingHandler instanceof FreeboxHandler) {
unregisterDiscoveryService(thingHandler.getThing());
} else if (thingHandler instanceof FreeboxThingHandler) {
unregisterAudioSink(thingHandler.getThing());
}
}
private synchronized void registerDiscoveryService(FreeboxHandler bridgeHandler) {
FreeboxDiscoveryService discoveryService = new FreeboxDiscoveryService(bridgeHandler);
discoveryService.activate(null);
discoveryServiceRegs.put(bridgeHandler.getThing().getUID(),
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
}
private synchronized void unregisterDiscoveryService(Thing thing) {
ServiceRegistration<?> serviceReg = discoveryServiceRegs.remove(thing.getUID());
if (serviceReg != null) {
// remove discovery service, if bridge handler is removed
FreeboxDiscoveryService service = (FreeboxDiscoveryService) bundleContext
.getService(serviceReg.getReference());
serviceReg.unregister();
if (service != null) {
service.deactivate();
}
}
}
private synchronized void registerAudioSink(FreeboxThingHandler thingHandler) {
String callbackUrl = createCallbackUrl();
FreeboxAirPlayAudioSink audioSink = new FreeboxAirPlayAudioSink(thingHandler, audioHTTPServer, callbackUrl);
@SuppressWarnings("unchecked")
ServiceRegistration<AudioSink> reg = (ServiceRegistration<AudioSink>) bundleContext
.registerService(AudioSink.class.getName(), audioSink, new Hashtable<>());
audioSinkRegistrations.put(thingHandler.getThing().getUID(), reg);
}
private synchronized void unregisterAudioSink(Thing thing) {
ServiceRegistration<AudioSink> reg = audioSinkRegistrations.remove(thing.getUID());
if (reg != null) {
reg.unregister();
}
}
private @Nullable String createCallbackUrl() {
if (callbackUrl != null) {
return callbackUrl;
} else {
String ipAddress = networkAddressService.getPrimaryIpv4HostAddress();
if (ipAddress == null) {
logger.warn("No network interface could be found.");
return null;
}
// we do not use SSL as it can cause certificate validation issues.
int port = HttpServiceUtil.getHttpServicePort(bundleContext);
if (port == -1) {
logger.warn("Cannot find port of the http service.");
return null;
}
return "http://" + ipAddress + ":" + port;
}
}
}

View File

@@ -0,0 +1,498 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.openhab.binding.freebox.internal.api.model.FreeboxAirMediaConfig;
import org.openhab.binding.freebox.internal.api.model.FreeboxAirMediaConfigResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxAirMediaReceiver;
import org.openhab.binding.freebox.internal.api.model.FreeboxAirMediaReceiverRequest;
import org.openhab.binding.freebox.internal.api.model.FreeboxAirMediaReceiversResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxAuthorizationStatus;
import org.openhab.binding.freebox.internal.api.model.FreeboxAuthorizationStatusResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxAuthorizeRequest;
import org.openhab.binding.freebox.internal.api.model.FreeboxAuthorizeResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxAuthorizeResult;
import org.openhab.binding.freebox.internal.api.model.FreeboxCallEntry;
import org.openhab.binding.freebox.internal.api.model.FreeboxCallEntryResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxConnectionStatus;
import org.openhab.binding.freebox.internal.api.model.FreeboxConnectionStatusResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxDiscoveryResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxEmptyResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxFtpConfig;
import org.openhab.binding.freebox.internal.api.model.FreeboxFtpConfigResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxFtthStatusResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxLanConfigResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxLanHost;
import org.openhab.binding.freebox.internal.api.model.FreeboxLanHostsResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxLanInterface;
import org.openhab.binding.freebox.internal.api.model.FreeboxLanInterfacesResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxLcdConfig;
import org.openhab.binding.freebox.internal.api.model.FreeboxLcdConfigResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxLoginResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxOpenSessionRequest;
import org.openhab.binding.freebox.internal.api.model.FreeboxOpenSessionResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxPhoneStatus;
import org.openhab.binding.freebox.internal.api.model.FreeboxPhoneStatusResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxSambaConfig;
import org.openhab.binding.freebox.internal.api.model.FreeboxSambaConfigResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxSystemConfig;
import org.openhab.binding.freebox.internal.api.model.FreeboxSystemConfigResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxUPnPAVConfig;
import org.openhab.binding.freebox.internal.api.model.FreeboxUPnPAVConfigResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxWifiGlobalConfig;
import org.openhab.binding.freebox.internal.api.model.FreeboxWifiGlobalConfigResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxXdslStatusResponse;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.util.HexUtils;
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.JsonSyntaxException;
/**
* The {@link FreeboxApiManager} is responsible for the communication with the Freebox.
* It implements the different HTTP API calls provided by the Freebox
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxApiManager {
private final Logger logger = LoggerFactory.getLogger(FreeboxApiManager.class);
private static final int HTTP_CALL_DEFAULT_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(10);
private static final String AUTH_HEADER = "X-Fbx-App-Auth";
private static final String HTTP_CALL_CONTENT_TYPE = "application/json; charset=utf-8";
private String appId;
private String appName;
private String appVersion;
private String deviceName;
private String baseAddress;
private String appToken;
private String sessionToken;
private Gson gson;
public FreeboxApiManager(String appId, String appName, String appVersion, String deviceName) {
this.appId = appId;
this.appName = appName;
this.appVersion = appVersion;
this.deviceName = deviceName;
this.gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
}
public FreeboxDiscoveryResponse checkApi(String fqdn, boolean secureHttp) {
String url = String.format("%s://%s/api_version", secureHttp ? "https" : "http", fqdn);
try {
String jsonResponse = HttpUtil.executeUrl("GET", url, HTTP_CALL_DEFAULT_TIMEOUT_MS);
return gson.fromJson(jsonResponse, FreeboxDiscoveryResponse.class);
} catch (IOException | JsonSyntaxException e) {
logger.debug("checkApi with {} failed: {}", url, e.getMessage());
return null;
}
}
public boolean authorize(boolean useHttps, String fqdn, String apiBaseUrl, String apiVersion, String appToken) {
String[] versionSplit = apiVersion.split("\\.");
String majorVersion = "5";
if (versionSplit.length > 0) {
majorVersion = versionSplit[0];
}
this.baseAddress = (useHttps ? "https://" : "http://") + fqdn + apiBaseUrl + "v" + majorVersion + "/";
boolean granted = false;
try {
String token = appToken;
if (token == null || token.isEmpty()) {
FreeboxAuthorizeRequest request = new FreeboxAuthorizeRequest(appId, appName, appVersion, deviceName);
FreeboxAuthorizeResult response = executePostUrl("login/authorize/", gson.toJson(request),
FreeboxAuthorizeResponse.class, false, false, true);
token = response.getAppToken();
int trackId = response.getTrackId();
FreeboxAuthorizationStatus result;
do {
Thread.sleep(2000);
result = executeGetUrl("login/authorize/" + trackId, FreeboxAuthorizationStatusResponse.class,
false);
} while (result.isStatusPending());
granted = result.isStatusGranted();
} else {
granted = true;
}
if (!granted) {
return false;
}
this.appToken = token;
openSession();
return true;
} catch (FreeboxException | InterruptedException e) {
logger.debug("Error while opening a session", e);
return false;
}
}
private synchronized void openSession() throws FreeboxException {
if (appToken == null) {
throw new FreeboxException("No app token to open a new session");
}
sessionToken = null;
String challenge = executeGetUrl("login/", FreeboxLoginResponse.class, false).getChallenge();
FreeboxOpenSessionRequest request = new FreeboxOpenSessionRequest(appId, hmacSha1(appToken, challenge));
sessionToken = executePostUrl("login/session/", gson.toJson(request), FreeboxOpenSessionResponse.class, false,
false, true).getSessionToken();
}
public synchronized void closeSession() {
if (sessionToken != null) {
try {
executePostUrl("login/logout/", null, FreeboxEmptyResponse.class, false);
} catch (FreeboxException e) {
}
sessionToken = null;
}
}
public String getAppToken() {
return appToken;
}
public synchronized String getSessionToken() {
return sessionToken;
}
public FreeboxConnectionStatus getConnectionStatus() throws FreeboxException {
return executeGetUrl("connection/", FreeboxConnectionStatusResponse.class);
}
public String getxDslStatus() throws FreeboxException {
return executeGetUrl("connection/xdsl/", FreeboxXdslStatusResponse.class).getStatus();
}
public boolean getFtthPresent() throws FreeboxException {
return executeGetUrl("connection/ftth/", FreeboxFtthStatusResponse.class).getSfpPresent();
}
public boolean isWifiEnabled() throws FreeboxException {
return executeGetUrl("wifi/config/", FreeboxWifiGlobalConfigResponse.class).isEnabled();
}
public boolean enableWifi(boolean enable) throws FreeboxException {
FreeboxWifiGlobalConfig config = new FreeboxWifiGlobalConfig();
config.setEnabled(enable);
return executePutUrl("wifi/config/", gson.toJson(config), FreeboxWifiGlobalConfigResponse.class).isEnabled();
}
public boolean isFtpEnabled() throws FreeboxException {
return executeGetUrl("ftp/config/", FreeboxFtpConfigResponse.class).isEnabled();
}
public boolean enableFtp(boolean enable) throws FreeboxException {
FreeboxFtpConfig config = new FreeboxFtpConfig();
config.setEnabled(enable);
return executePutUrl("ftp/config/", gson.toJson(config), FreeboxFtpConfigResponse.class).isEnabled();
}
public boolean isAirMediaEnabled() throws FreeboxException {
return executeGetUrl("airmedia/config/", FreeboxAirMediaConfigResponse.class).isEnabled();
}
public boolean enableAirMedia(boolean enable) throws FreeboxException {
FreeboxAirMediaConfig config = new FreeboxAirMediaConfig();
config.setEnabled(enable);
return executePutUrl("airmedia/config/", gson.toJson(config), FreeboxAirMediaConfigResponse.class).isEnabled();
}
public boolean isUPnPAVEnabled() throws FreeboxException {
return executeGetUrl("upnpav/config/", FreeboxUPnPAVConfigResponse.class).isEnabled();
}
public boolean enableUPnPAV(boolean enable) throws FreeboxException {
FreeboxUPnPAVConfig config = new FreeboxUPnPAVConfig();
config.setEnabled(enable);
return executePutUrl("upnpav/config/", gson.toJson(config), FreeboxUPnPAVConfigResponse.class).isEnabled();
}
public FreeboxSambaConfig getSambaConfig() throws FreeboxException {
return executeGetUrl("netshare/samba/", FreeboxSambaConfigResponse.class);
}
public boolean enableSambaFileShare(boolean enable) throws FreeboxException {
FreeboxSambaConfig config = new FreeboxSambaConfig();
config.setFileShareEnabled(enable);
return executePutUrl("netshare/samba/", gson.toJson(config), FreeboxSambaConfigResponse.class)
.isFileShareEnabled();
}
public boolean enableSambaPrintShare(boolean enable) throws FreeboxException {
FreeboxSambaConfig config = new FreeboxSambaConfig();
config.setPrintShareEnabled(enable);
return executePutUrl("netshare/samba/", gson.toJson(config), FreeboxSambaConfigResponse.class)
.isPrintShareEnabled();
}
public FreeboxLcdConfig getLcdConfig() throws FreeboxException {
return executeGetUrl("lcd/config/", FreeboxLcdConfigResponse.class);
}
public int setLcdBrightness(int brightness) throws FreeboxException {
FreeboxLcdConfig config = getLcdConfig();
int newValue = Math.min(100, brightness);
newValue = Math.max(newValue, 0);
config.setBrightness(newValue);
return executePutUrl("lcd/config/", gson.toJson(config), FreeboxLcdConfigResponse.class).getBrightness();
}
public int increaseLcdBrightness() throws FreeboxException {
FreeboxLcdConfig config = getLcdConfig();
config.setBrightness(Math.min(100, config.getBrightness() + 1));
return executePutUrl("lcd/config/", gson.toJson(config), FreeboxLcdConfigResponse.class).getBrightness();
}
public int decreaseLcdBrightness() throws FreeboxException {
FreeboxLcdConfig config = getLcdConfig();
config.setBrightness(Math.max(0, config.getBrightness() - 1));
return executePutUrl("lcd/config/", gson.toJson(config), FreeboxLcdConfigResponse.class).getBrightness();
}
public FreeboxLcdConfig setLcdOrientation(int orientation) throws FreeboxException {
FreeboxLcdConfig config = getLcdConfig();
int newValue = Math.min(360, orientation);
newValue = Math.max(newValue, 0);
config.setOrientation(newValue);
config.setOrientationForced(true);
return executePutUrl("lcd/config/", gson.toJson(config), FreeboxLcdConfigResponse.class);
}
public boolean setLcdOrientationForced(boolean forced) throws FreeboxException {
FreeboxLcdConfig config = getLcdConfig();
config.setOrientationForced(forced);
return executePutUrl("lcd/config/", gson.toJson(config), FreeboxLcdConfigResponse.class).isOrientationForced();
}
public FreeboxSystemConfig getSystemConfig() throws FreeboxException {
return executeGetUrl("system/", FreeboxSystemConfigResponse.class);
}
public boolean isInLanBridgeMode() throws FreeboxException {
return executeGetUrl("lan/config/", FreeboxLanConfigResponse.class).isInBridgeMode();
}
public List<FreeboxLanHost> getLanHosts() throws FreeboxException {
List<FreeboxLanHost> hosts = new ArrayList<>();
List<FreeboxLanInterface> interfaces = executeGetUrl("lan/browser/interfaces/",
FreeboxLanInterfacesResponse.class);
if (interfaces != null) {
for (FreeboxLanInterface lanInterface : interfaces) {
if (lanInterface.getHostCount() > 0) {
List<FreeboxLanHost> lanHosts = getLanHostsFromInterface(lanInterface.getName());
if (lanHosts != null) {
hosts.addAll(lanHosts);
}
}
}
}
return hosts;
}
private List<FreeboxLanHost> getLanHostsFromInterface(String lanInterface) throws FreeboxException {
return executeGetUrl("lan/browser/" + encodeUrl(lanInterface) + "/", FreeboxLanHostsResponse.class);
}
public FreeboxPhoneStatus getPhoneStatus() throws FreeboxException {
// This API is undocumented but working
// It is extracted from the freeboxos-java library
// https://github.com/MatMaul/freeboxos-java/blob/master/src/org/matmaul/freeboxos/phone/PhoneManager.java#L17
return executeGetUrl("phone/?_dc=1415032391207", FreeboxPhoneStatusResponse.class).get(0);
}
public List<FreeboxCallEntry> getCallEntries() throws FreeboxException {
return executeGetUrl("call/log/", FreeboxCallEntryResponse.class);
}
public List<FreeboxAirMediaReceiver> getAirMediaReceivers() throws FreeboxException {
return executeGetUrl("airmedia/receivers/", FreeboxAirMediaReceiversResponse.class, true, true, false);
}
public void playMedia(String url, String airPlayName, String airPlayPassword) throws FreeboxException {
FreeboxAirMediaReceiverRequest request = new FreeboxAirMediaReceiverRequest();
request.setStartAction();
request.setVideoMediaType();
if (airPlayPassword != null && !airPlayPassword.isEmpty()) {
request.setPassword(airPlayPassword);
}
request.setMedia(url);
executePostUrl("airmedia/receivers/" + encodeUrl(airPlayName) + "/", gson.toJson(request),
FreeboxEmptyResponse.class, true, false, true);
}
public void stopMedia(String airPlayName, String airPlayPassword) throws FreeboxException {
FreeboxAirMediaReceiverRequest request = new FreeboxAirMediaReceiverRequest();
request.setStopAction();
request.setVideoMediaType();
if (airPlayPassword != null && !airPlayPassword.isEmpty()) {
request.setPassword(airPlayPassword);
}
executePostUrl("airmedia/receivers/" + encodeUrl(airPlayName) + "/", gson.toJson(request),
FreeboxEmptyResponse.class, true, false, true);
}
public void reboot() throws FreeboxException {
executePostUrl("system/reboot/", null, FreeboxEmptyResponse.class);
}
private <T extends FreeboxResponse<F>, F> F executeGetUrl(String relativeUrl, Class<T> responseClass)
throws FreeboxException {
return executeUrl("GET", relativeUrl, null, responseClass, true, false, false);
}
private <T extends FreeboxResponse<F>, F> F executeGetUrl(String relativeUrl, Class<T> responseClass,
boolean retryAuth) throws FreeboxException {
return executeUrl("GET", relativeUrl, null, responseClass, retryAuth, false, false);
}
private <T extends FreeboxResponse<F>, F> F executeGetUrl(String relativeUrl, Class<T> responseClass,
boolean retryAuth, boolean patchTableReponse, boolean doNotLogData) throws FreeboxException {
return executeUrl("GET", relativeUrl, null, responseClass, retryAuth, patchTableReponse, doNotLogData);
}
private <T extends FreeboxResponse<F>, F> F executePostUrl(String relativeUrl, String requestContent,
Class<T> responseClass) throws FreeboxException {
return executeUrl("POST", relativeUrl, requestContent, responseClass, true, false, false);
}
private <T extends FreeboxResponse<F>, F> F executePostUrl(String relativeUrl, String requestContent,
Class<T> responseClass, boolean retryAuth) throws FreeboxException {
return executeUrl("POST", relativeUrl, requestContent, responseClass, retryAuth, false, false);
}
private <T extends FreeboxResponse<F>, F> F executePostUrl(String relativeUrl, String requestContent,
Class<T> responseClass, boolean retryAuth, boolean patchTableReponse, boolean doNotLogData)
throws FreeboxException {
return executeUrl("POST", relativeUrl, requestContent, responseClass, retryAuth, patchTableReponse,
doNotLogData);
}
private <T extends FreeboxResponse<F>, F> F executePutUrl(String relativeUrl, String requestContent,
Class<T> responseClass) throws FreeboxException {
return executeUrl("PUT", relativeUrl, requestContent, responseClass, true, false, false);
}
private <T extends FreeboxResponse<F>, F> F executeUrl(String httpMethod, String relativeUrl, String requestContent,
Class<T> responseClass, boolean retryAuth, boolean patchTableReponse, boolean doNotLogData)
throws FreeboxException {
try {
Properties headers = null;
String token = getSessionToken();
if (token != null) {
headers = new Properties();
headers.setProperty(AUTH_HEADER, token);
}
InputStream stream = null;
String contentType = null;
if (requestContent != null) {
stream = new ByteArrayInputStream(requestContent.getBytes(StandardCharsets.UTF_8));
contentType = HTTP_CALL_CONTENT_TYPE;
}
logger.debug("executeUrl {} {} requestContent {}", httpMethod, relativeUrl,
doNotLogData ? "***" : requestContent);
String jsonResponse = HttpUtil.executeUrl(httpMethod, baseAddress + relativeUrl, headers, stream,
contentType, HTTP_CALL_DEFAULT_TIMEOUT_MS);
if (stream != null) {
stream.close();
stream = null;
}
if (patchTableReponse) {
// Replace empty result by an empty table result
jsonResponse = jsonResponse.replace("\"result\":{}", "\"result\":[]");
}
return evaluateJsonReesponse(jsonResponse, responseClass, doNotLogData);
} catch (FreeboxException e) {
if (retryAuth && e.isAuthRequired()) {
logger.debug("Authentication required: open a new session and retry the request");
openSession();
return executeUrl(httpMethod, relativeUrl, requestContent, responseClass, false, patchTableReponse,
doNotLogData);
}
throw e;
} catch (IOException e) {
throw new FreeboxException(httpMethod + " request " + relativeUrl + ": execution failed: " + e.getMessage(),
e);
} catch (JsonSyntaxException e) {
throw new FreeboxException(
httpMethod + " request " + relativeUrl + ": response parsing failed: " + e.getMessage(), e);
}
}
private <T extends FreeboxResponse<F>, F> F evaluateJsonReesponse(String jsonResponse, Class<T> responseClass,
boolean doNotLogData) throws JsonSyntaxException, FreeboxException {
logger.debug("evaluateJsonReesponse Json {}", doNotLogData ? "***" : jsonResponse);
// First check only if the result is successful
FreeboxResponse<Object> partialResponse = gson.fromJson(jsonResponse, FreeboxEmptyResponse.class);
partialResponse.evaluate();
// Parse the full response in case of success
T fullResponse = gson.fromJson(jsonResponse, responseClass);
fullResponse.evaluate();
F result = fullResponse.getResult();
return result;
}
private String encodeUrl(String url) throws FreeboxException {
try {
return URLEncoder.encode(url, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
throw new FreeboxException("Encoding the URL \"" + url + "\" in UTF-8 failed", e);
}
}
public static String hmacSha1(String key, String value) throws FreeboxException {
try {
// Get an hmac_sha1 key from the raw key bytes
byte[] keyBytes = key.getBytes();
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
// Get an hmac_sha1 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
// Compute the hmac on input data bytes
byte[] rawHmac = mac.doFinal(value.getBytes());
// Convert raw bytes to a String
return HexUtils.bytesToHex(rawHmac).toLowerCase();
} catch (IllegalArgumentException | NoSuchAlgorithmException | InvalidKeyException | IllegalStateException e) {
throw new FreeboxException("Computing the hmac-sha1 of the challenge and the app token failed", e);
}
}
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api;
import org.openhab.binding.freebox.internal.api.model.FreeboxResponse;
/**
* Exception for errors when using the Freebox API
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxException extends Exception {
private static final long serialVersionUID = 1L;
protected FreeboxResponse<?> response;
public FreeboxException(String msg) {
this(msg, null, null);
}
public FreeboxException(String msg, Throwable cause) {
this(msg, cause, null);
}
public FreeboxException(String msg, FreeboxResponse<?> response) {
this(msg, null, response);
}
public FreeboxException(FreeboxResponse<?> response) {
this(response.getMsg(), null, response);
}
public FreeboxException(String msg, Throwable cause, FreeboxResponse<?> response) {
super(msg, cause);
this.response = response;
}
public FreeboxResponse<?> getResponse() {
return response;
}
public boolean isAuthRequired() {
return getResponse() != null && getResponse().isAuthRequired();
}
public boolean isMissingRights() {
return getResponse() != null && getResponse().isMissingRights();
}
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api;
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 "mafreebox.freebox.fr";
}
@Override
public URL getCertificate() {
URL resource = Thread.currentThread().getContextClassLoader().getResource("freeboxECCRootCA.crt");
if (resource != null) {
return resource;
} else {
throw new IllegalStateException("Certifcate resource not found or not accessible");
}
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxAirMediaConfig} 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
*/
public class FreeboxAirMediaConfig {
private Boolean enabled;
private String password;
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxAirMediaConfigResponse} is the Java class used to map the
* response of the AirMedia configuration API
* https://dev.freebox.fr/sdk/os/airmedia/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxAirMediaConfigResponse extends FreeboxResponse<FreeboxAirMediaConfig> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in AirMedia configuration API response", this);
}
if (getResult().isEnabled() == null) {
throw new FreeboxException("No AirMedia status in response", this);
}
}
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxAirMediaReceiver} 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
*/
public class FreeboxAirMediaReceiver {
private String name;
private boolean passwordProtected;
private FreeboxAirMediaReceiverCapabilities capabilities;
public String getName() {
return name;
}
public boolean isPasswordProtected() {
return passwordProtected;
}
public FreeboxAirMediaReceiverCapabilities getCapabilities() {
return capabilities;
}
public boolean isPhotoCapable() {
return capabilities.isPhotoCapable();
}
public boolean isScreenCapable() {
return capabilities.isScreenCapable();
}
public boolean isVideoCapable() {
return capabilities.isVideoCapable();
}
public boolean isAudioCapable() {
return capabilities.isAudioCapable();
}
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxAirMediaReceiverCapabilities} is the Java class used to map the
* structure used inside the response of the available AirMedia receivers API to provide
* the receiver capabilities
* https://dev.freebox.fr/sdk/os/airmedia/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxAirMediaReceiverCapabilities {
private boolean photo;
private boolean screen;
private boolean video;
private boolean audio;
public boolean isPhotoCapable() {
return photo;
}
public boolean isScreenCapable() {
return screen;
}
public boolean isVideoCapable() {
return video;
}
public boolean isAudioCapable() {
return audio;
}
}

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxAirMediaReceiverRequest} 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
*/
public class FreeboxAirMediaReceiverRequest {
private static enum MediaAction {
START("start"),
STOP("stop");
private String action;
private MediaAction(String action) {
this.action = action;
}
public String getAction() {
return action;
}
}
private static enum MediaType {
VIDEO("video"),
PHOTO("photo");
private String mediaType;
private MediaType(String mediaType) {
this.mediaType = mediaType;
}
public String getMediaType() {
return mediaType;
}
}
private String action;
private String mediaType;
private String password;
private Integer position;
private String media;
public void setStartAction() {
this.action = MediaAction.START.getAction();
}
public void setStopAction() {
this.action = MediaAction.STOP.getAction();
}
public void setVideoMediaType() {
this.mediaType = MediaType.VIDEO.getMediaType();
}
public void setPassword(String password) {
this.password = password;
}
public void setPosition(Integer position) {
this.position = position;
}
public void setMedia(String media) {
this.media = media;
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import java.util.List;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxAirMediaReceiversResponse} is the Java class used to map the
* response of the available AirMedia receivers API
* https://dev.freebox.fr/sdk/os/airmedia/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxAirMediaReceiversResponse extends FreeboxResponse<List<FreeboxAirMediaReceiver>> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in available AirMedia receivers API response", this);
}
}
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxAuthorizationStatus} is the Java class used to map the
* structure used by the response of the track authorization progress API
* https://dev.freebox.fr/sdk/os/login/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxAuthorizationStatus {
private static enum AuthorizationStatus {
UNKNOWN("unknown"),
PENDING("pending"),
TIMEOUT("timeout"),
GRANTED("granted"),
DENIED("denied");
private String status;
private AuthorizationStatus(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
}
private String status;
private String challenge;
public boolean isStatusPending() {
return AuthorizationStatus.PENDING.getStatus().equalsIgnoreCase(status);
}
public boolean isStatusGranted() {
return AuthorizationStatus.GRANTED.getStatus().equalsIgnoreCase(status);
}
public String getStatus() {
return status;
}
public String getChallenge() {
return challenge;
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxAuthorizationStatusResponse} is the Java class used to map the
* response of the track authorization progress API
* https://dev.freebox.fr/sdk/os/login/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxAuthorizationStatusResponse extends FreeboxResponse<FreeboxAuthorizationStatus> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in track authorization progress API response", this);
}
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxAuthorizeRequest} is the Java class used to map the
* structure used by the request of the request authorization API
* https://dev.freebox.fr/sdk/os/login/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxAuthorizeRequest {
private String appId;
private String appName;
private String appVersion;
private String deviceName;
public FreeboxAuthorizeRequest(String appId, String appName, String appVersion, String deviceName) {
this.appId = appId;
this.appName = appName;
this.appVersion = appVersion;
this.deviceName = deviceName;
}
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxAuthorizeResponse} is the Java class used to map the
* response of the request authorization API
* https://dev.freebox.fr/sdk/os/login/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxAuthorizeResponse extends FreeboxResponse<FreeboxAuthorizeResult> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in request authorization API response", this);
}
if ((getResult().getAppToken() == null) || getResult().getAppToken().isEmpty()) {
throw new FreeboxException("No app token in response", this);
}
if (getResult().getTrackId() == null) {
throw new FreeboxException("No track id in response", this);
}
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxAuthorizeResult} 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
*/
public class FreeboxAuthorizeResult {
private String appToken;
private Integer trackId;
public String getAppToken() {
return appToken;
}
public Integer getTrackId() {
return trackId;
}
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import java.util.Calendar;
import com.google.gson.annotations.SerializedName;
/**
* The {@link FreeboxCallEntry} 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
*/
public class FreeboxCallEntry {
private static final String CALL_ENTRY_TYPE_ACCEPTED = "accepted";
private static final String CALL_ENTRY_TYPE_MISSED = "missed";
private static final String CALL_ENTRY_TYPE_OUTGOING = "outgoing";
private int id;
private String type;
private long datetime;
private String number;
private String name;
private int duration;
@SerializedName("new")
private boolean newCall;
private int contactId;
public Calendar getTimeStamp() {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(datetime * 1000);
return c;
}
public boolean isAccepted() {
return CALL_ENTRY_TYPE_ACCEPTED.equalsIgnoreCase(type);
}
public boolean isMissed() {
return CALL_ENTRY_TYPE_MISSED.equalsIgnoreCase(type);
}
public boolean isOutGoing() {
return CALL_ENTRY_TYPE_OUTGOING.equalsIgnoreCase(type);
}
public int getId() {
return id;
}
public String getType() {
return type;
}
public long getDatetime() {
return datetime;
}
public String getNumber() {
return number;
}
public String getName() {
return name;
}
public int getDuration() {
return duration;
}
public boolean isNewCall() {
return newCall;
}
public int getContactId() {
return contactId;
}
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import java.util.List;
/**
* The {@link FreeboxCallEntryResponse} is the Java class used to map the
* response of the call API
* https://dev.freebox.fr/sdk/os/call/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxCallEntryResponse extends FreeboxResponse<List<FreeboxCallEntry>> {
}

View File

@@ -0,0 +1,87 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxConnectionStatus} 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
*/
public class FreeboxConnectionStatus {
private String state;
private String type;
private String media;
private String ipv4;
private String ipv6;
private long rateUp;
private long rateDown;
private long bandwidthUp;
private long bandwidthDown;
private long bytesUp;
private long bytesDown;
private Long[] ipv4PortRange;
public String getState() {
return state;
}
public String getType() {
return type;
}
public String getMedia() {
return media;
}
public String getIpv4() {
return ipv4;
}
public String getIpv6() {
return ipv6;
}
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;
}
public Long getIpvPortMin() {
return ipv4PortRange[0];
}
public Long getIpvPortMax() {
return ipv4PortRange[1];
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxConnectionStatusResponse} is the Java class used to map the
* response of the connection API
* https://dev.freebox.fr/sdk/os/connection/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxConnectionStatusResponse extends FreeboxResponse<FreeboxConnectionStatus> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in connection API response", this);
}
}
}

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxDiscoveryResponse} is the Java class used to map the
* structure used by the response of the request authorization API
* https://dev.freebox.fr/sdk/os/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxDiscoveryResponse {
private String uid;
private String deviceName;
private String apiVersion;
private String apiBaseUrl;
private String deviceType;
private String apiDomain;
private Boolean httpsAvailable;
private Integer httpsPort;
public String getUid() {
return uid;
}
public String getDeviceName() {
return deviceName;
}
public String getApiVersion() {
return apiVersion;
}
public String getApiBaseUrl() {
return apiBaseUrl;
}
public String getDeviceType() {
return deviceType;
}
public String getApiDomain() {
return apiDomain;
}
public Boolean isHttpsAvailable() {
return httpsAvailable;
}
public Integer getHttpsPort() {
return httpsPort;
}
}

View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxEmptyResponse} is the Java class used to map an empty response
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxEmptyResponse extends FreeboxResponse<Object> {
}

View File

@@ -0,0 +1,100 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxFtpConfig} 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
*/
public class FreeboxFtpConfig {
private Boolean enabled;
private Boolean allowAnonymous;
private Boolean allowAnonymousWrite;
private String password;
private Boolean allowRemoteAccess;
private Boolean weakPassword;
private Integer portCtrl;
private Integer portData;
private String remoteDomain;
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Boolean isAllowAnonymous() {
return allowAnonymous;
}
public void setAllowAnonymous(Boolean allowAnonymous) {
this.allowAnonymous = allowAnonymous;
}
public Boolean isAllowAnonymousWrite() {
return allowAnonymousWrite;
}
public void setAllowAnonymousWrite(Boolean allowAnonymousWrite) {
this.allowAnonymousWrite = allowAnonymousWrite;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean isAllowRemoteAccess() {
return allowRemoteAccess;
}
public void setAllowRemoteAccess(Boolean allowRemoteAccess) {
this.allowRemoteAccess = allowRemoteAccess;
}
public Boolean isWeakPassword() {
return weakPassword;
}
public void setWeakPassword(Boolean weakPassword) {
this.weakPassword = weakPassword;
}
public Integer getPortCtrl() {
return portCtrl;
}
public void setPortCtrl(Integer portCtrl) {
this.portCtrl = portCtrl;
}
public Integer getPortData() {
return portData;
}
public void setPortData(Integer portData) {
this.portData = portData;
}
public String getRemoteDomain() {
return remoteDomain;
}
public void setRemoteDomain(String remoteDomain) {
this.remoteDomain = remoteDomain;
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxFtpConfigResponse} is the Java class used to map the
* response of the FTP configuration API
* https://dev.freebox.fr/sdk/os/ftp/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxFtpConfigResponse extends FreeboxResponse<FreeboxFtpConfig> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in FTP configuration API response", this);
}
if (getResult().isEnabled() == null) {
throw new FreeboxException("No FTP status in response", this);
}
}
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxFtthStatus} is the Java class used to map the "FtthStatus"
* structure used by the response of the connection Ftth status API
* https://dev.freebox.fr/sdk/os/connection/#
*
* @author Gaël L'hopital - Initial contribution
*/
public class FreeboxFtthStatus {
private boolean sfp_present;
private boolean sfp_alim_ok;
private boolean sfp_has_power_report;
private boolean sfp_has_signal;
private boolean link;
private String sfp_serial;
private String sfp_model;
private String sfp_vendor;
private long sfp_pwr_tx;
private long sfp_pwr_rx;
public boolean getSfpPresent() {
return sfp_present;
}
public boolean getSfpAlimOk() {
return sfp_alim_ok;
}
public boolean getSfpHasPowerReport() {
return sfp_has_power_report;
}
public boolean getSfpHasSignal() {
return sfp_has_signal;
}
public boolean getLink() {
return link;
}
public String getSfpSerial() {
return sfp_serial;
}
public String getSfpModel() {
return sfp_model;
}
public String getSfpVendor() {
return sfp_vendor;
}
public long getSfpPwrTx() {
return sfp_pwr_tx;
}
public long getSfpPwrRx() {
return sfp_pwr_rx;
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxFtthStatusResponse} is the Java class used to map the
* response of the connection Ftth status API
* https://dev.freebox.fr/sdk/os/connection/#
*
* @author Gaël L'hopital - Initial contribution
*/
public class FreeboxFtthStatusResponse extends FreeboxResponse<FreeboxFtthStatus> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in connection Ftth status API response", this);
}
}
}

View File

@@ -0,0 +1,59 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxLanConfig} is the Java class used to map the "LanConfig"
* structure used by the LAN configuration API
* https://dev.freebox.fr/sdk/os/lan/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxLanConfig {
private static final String LAN_BRIDGE_MODE = "bridge";
private String ip;
private String name;
private String nameDns;
private String nameMdns;
private String nameNetbios;
private String type;
public boolean isInBridgeMode() {
return LAN_BRIDGE_MODE.equalsIgnoreCase(type);
}
public String getIp() {
return ip;
}
public String getName() {
return name;
}
public String getNameDns() {
return nameDns;
}
public String getNameMdns() {
return nameMdns;
}
public String getNameNetbios() {
return nameNetbios;
}
public String getType() {
return type;
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxLanConfigResponse} is the Java class used to map the
* response of the LAN configuration API
* https://dev.freebox.fr/sdk/os/lan/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxLanConfigResponse extends FreeboxResponse<FreeboxLanConfig> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in LAN configuration API response", this);
}
}
}

View File

@@ -0,0 +1,94 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import java.util.List;
/**
* The {@link FreeboxLanHost} 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
*/
public class FreeboxLanHost {
private String id;
private String primaryName;
private String hostType;
private boolean primaryNameManual;
private FreeboxLanHostL2Ident l2ident;
private String vendorName;
private boolean persistent;
private boolean reachable;
private long lastTimeReachable;
private boolean active;
private long lastActivity;
private List<FreeboxLanHostName> names;
private List<FreeboxLanHostL3Connectivity> l3connectivities;
public String getMAC() {
return (l2ident != null && l2ident.isMacAddress()) ? l2ident.getId() : null;
}
public String getId() {
return id;
}
public String getPrimaryName() {
return primaryName;
}
public String getHostType() {
return hostType;
}
public boolean isPrimaryNameManual() {
return primaryNameManual;
}
public FreeboxLanHostL2Ident getL2Ident() {
return l2ident;
}
public String getVendorName() {
return vendorName;
}
public boolean isPersistent() {
return persistent;
}
public boolean isReachable() {
return reachable;
}
public long getLastTimeReachable() {
return lastTimeReachable;
}
public boolean isActive() {
return active;
}
public long getLastActivity() {
return lastActivity;
}
public List<FreeboxLanHostName> getNames() {
return names;
}
public List<FreeboxLanHostL3Connectivity> getL3Connectivities() {
return l3connectivities;
}
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxLanHostL2Ident} 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
*/
public class FreeboxLanHostL2Ident {
private static final String L2_TYPE_MAC_ADDRESS = "mac_address";
private String id;
private String type;
public boolean isMacAddress() {
return L2_TYPE_MAC_ADDRESS.equalsIgnoreCase(type);
}
public String getId() {
return id;
}
public String getType() {
return type;
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxLanHostL3Connectivity} 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
*/
public class FreeboxLanHostL3Connectivity {
private String addr;
private String af;
private boolean active;
private boolean reachable;
private long lastActivity;
private long lastTimeReachable;
public String getAddr() {
return addr;
}
public String getAf() {
return af;
}
public boolean isActive() {
return active;
}
public boolean isReachable() {
return reachable;
}
public long getLastActivity() {
return lastActivity;
}
public long getLastTimeReachable() {
return lastTimeReachable;
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxLanHostName} is the Java class used to map the "LanHostName"
* structure used by the Lan Hosts Browser API
* https://dev.freebox.fr/sdk/os/lan/#lan-browser
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxLanHostName {
private String name;
private String source;
public String getName() {
return name;
}
public String getSource() {
return source;
}
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import java.util.List;
/**
* The {@link FreeboxLanHostsResponse} is the Java class used to map the
* response of Lan Hosts Browser API
* https://dev.freebox.fr/sdk/os/lan/#lan-browser
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxLanHostsResponse extends FreeboxResponse<List<FreeboxLanHost>> {
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxLanInterface} 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
*/
public class FreeboxLanInterface {
private String name;
private int hostCount;
public String getName() {
return name;
}
public int getHostCount() {
return hostCount;
}
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import java.util.List;
/**
* The {@link FreeboxLanInterfacesResponse} is the Java class used to map the
* response of Lan Interface Browser API
* https://dev.freebox.fr/sdk/os/lan/#lan-browser
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxLanInterfacesResponse extends FreeboxResponse<List<FreeboxLanInterface>> {
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxLcdConfig} 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
*/
public class FreeboxLcdConfig {
private int brightness;
private boolean orientationForced;
private int orientation;
public int getBrightness() {
return brightness;
}
public void setBrightness(int brightness) {
this.brightness = 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 = orientation;
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxLcdConfigResponse} is the Java class used to map the
* response of the LCD configuration API
* https://dev.freebox.fr/sdk/os/lcd/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxLcdConfigResponse extends FreeboxResponse<FreeboxLcdConfig> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in LCD configuration API response", this);
}
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxLoginResponse} is the Java class used to map the
* response of the login API
* https://dev.freebox.fr/sdk/os/login/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxLoginResponse extends FreeboxResponse<FreeboxLoginResult> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in login API response", this);
}
if ((getResult().getChallenge() == null) || getResult().getChallenge().isEmpty()) {
throw new FreeboxException("No challenge in response", this);
}
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxLoginResult} is the Java class used to map the
* structure used by the response of the login API
* https://dev.freebox.fr/sdk/os/login/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxLoginResult {
private Boolean loggedIn;
private String challenge;
public Boolean isLoggedIn() {
return loggedIn;
}
public String getChallenge() {
return challenge;
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxOpenSessionRequest} is the Java class used to map the
* structure used by the request of the open session API
* https://dev.freebox.fr/sdk/os/login/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxOpenSessionRequest {
private String appId;
private String password;
public FreeboxOpenSessionRequest(String appId, String password) {
this.appId = appId;
this.password = password;
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxOpenSessionResponse} is the Java class used to map the
* response of the open session API
* https://dev.freebox.fr/sdk/os/login/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxOpenSessionResponse extends FreeboxResponse<FreeboxOpenSessionResult> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in open session API response", this);
}
if ((getResult().getSessionToken() == null) || getResult().getSessionToken().isEmpty()) {
throw new FreeboxException("No session token in response", this);
}
}
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxOpenSessionResult} 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
*/
public class FreeboxOpenSessionResult {
private String sessionToken;
private String challenge;
private FreeboxPermissions permissions;
public String getSessionToken() {
return sessionToken;
}
public String getChallenge() {
return challenge;
}
public FreeboxPermissions getPermissions() {
return permissions;
}
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxPermissions} is the Java class used to map the
* structure used inside the response of the open session API to provide
* the permissions
* https://dev.freebox.fr/sdk/os/login/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxPermissions {
private Boolean settings;
private Boolean contacts;
private Boolean calls;
private Boolean explorer;
private Boolean downloader;
private Boolean parental;
private Boolean pvr;
private Boolean tv;
public Boolean isSettingsAllowed() {
return settings;
}
public Boolean isContactsAllowed() {
return contacts;
}
public Boolean isCallsAllowed() {
return calls;
}
public Boolean isExplorerAllowed() {
return explorer;
}
public Boolean isDownloaderAllowed() {
return downloader;
}
public Boolean isParentalAllowed() {
return parental;
}
public Boolean isPvrAllowed() {
return pvr;
}
public Boolean istTvAllowed() {
return tv;
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxPhoneStatus} is the Java class used to map the
* structure used by the phone API
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxPhoneStatus {
private boolean isRinging;
private boolean onHook;
public boolean isRinging() {
return isRinging;
}
public boolean isOnHook() {
return onHook;
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import java.util.List;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxPhoneStatusResponse} is the Java class used to map the
* response of the phone API
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxPhoneStatusResponse extends FreeboxResponse<List<FreeboxPhoneStatus>> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null || getResult().isEmpty()) {
throw new FreeboxException("No phone status in response", this);
}
}
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxResponse} is the Java class used to map the "APIResponse"
* structure used by the API
* https://dev.freebox.fr/sdk/os/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxResponse<T> {
private static final String AUTHORIZATION_REQUIRED = "auth_required";
private static final String INSUFFICIENT_RIGHTS = "insufficient_rights";
private Boolean success;
private String errorCode;
private String uid;
private String msg;
private String missingRight;
private T result;
public void evaluate() throws FreeboxException {
if (!isSuccess()) {
throw new FreeboxException(this);
}
}
public boolean isAuthRequired() {
return AUTHORIZATION_REQUIRED.equalsIgnoreCase(errorCode);
}
public boolean isMissingRights() {
return INSUFFICIENT_RIGHTS.equalsIgnoreCase(errorCode);
}
public Boolean isSuccess() {
return success;
}
public String getErrorCode() {
return errorCode;
}
public String getUid() {
return uid;
}
public String getMsg() {
return msg;
}
public String getMissingRight() {
return missingRight;
}
public T getResult() {
return result;
}
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxSambaConfig} is the Java class used to map the "SambaConfig"
* structure used by the Samba configuration API
* https://dev.freebox.fr/sdk/os/network_share/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxSambaConfig {
private Boolean fileShareEnabled;
private Boolean printShareEnabled;
private Boolean logonEnabled;
private String logonUser;
private String logonPassword;
private String workgroup;
public Boolean isFileShareEnabled() {
return fileShareEnabled;
}
public void setFileShareEnabled(Boolean fileShareEnabled) {
this.fileShareEnabled = fileShareEnabled;
}
public Boolean isPrintShareEnabled() {
return printShareEnabled;
}
public void setPrintShareEnabled(Boolean printShareEnabled) {
this.printShareEnabled = printShareEnabled;
}
public Boolean isLogonEnabled() {
return logonEnabled;
}
public void setLogonEnabled(Boolean logonEnabled) {
this.logonEnabled = logonEnabled;
}
public String getLogonUser() {
return logonUser;
}
public void setLogonUser(String logonUser) {
this.logonUser = logonUser;
}
public String getWorkgroup() {
return workgroup;
}
public void setWorkgroup(String workgroup) {
this.workgroup = workgroup;
}
public void setLogonPassword(String logonPassword) {
this.logonPassword = logonPassword;
}
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxSambaConfigResponse} is the Java class used to map the
* response of the Samba configuration API
* https://dev.freebox.fr/sdk/os/network_share/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxSambaConfigResponse extends FreeboxResponse<FreeboxSambaConfig> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in Samba configuration API response", this);
}
if (getResult().isFileShareEnabled() == null) {
throw new FreeboxException("No file sharing status in response", this);
}
if (getResult().isPrintShareEnabled() == null) {
throw new FreeboxException("No printer sharing status in response", this);
}
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxSensor} is the Java class used to map the fans and sensors part of the "SystemConfig"
* structure used by the system API
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxSensor {
private String id;
private String name;
private int value;
public String getId() {
return id;
}
public String getName() {
return name;
}
public int getValue() {
return value;
}
}

View File

@@ -0,0 +1,125 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import java.util.List;
/**
* The {@link FreeboxSystemConfig} is the Java class used to map the "SystemConfig"
* structure used by the system API
* https://dev.freebox.fr/sdk/os/system/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxSystemConfig {
private String firmwareVersion;
private String mac;
private String serial;
private String uptime;
private long uptimeVal;
private String boardName;
private int tempCpum;
private int tempSw;
private int tempCpub;
private int fanRpm;
private boolean boxAuthenticated;
private String diskStatus;
private String boxFlavor;
private String userMainStorage;
private List<FreeboxSensor> fans;
private List<FreeboxSensor> sensors;
public String getFirmwareVersion() {
return firmwareVersion;
}
public String getMac() {
return mac;
}
public String getSerial() {
return serial;
}
public String getUptime() {
return uptime;
}
public long getUptimeVal() {
return uptimeVal;
}
public String getBoardName() {
return boardName;
}
public int getTempCpum() {
if (sensors != null) {
for (FreeboxSensor sensor : sensors) {
if ("temp_cpum".equals(sensor.getId())) {
return sensor.getValue();
}
}
}
return tempCpum;
}
public int getTempSw() {
if (sensors != null) {
for (FreeboxSensor sensor : sensors) {
if ("temp_sw".equals(sensor.getId())) {
return sensor.getValue();
}
}
}
return tempSw;
}
public int getTempCpub() {
if (sensors != null) {
for (FreeboxSensor sensor : sensors) {
if ("temp_cpub".equals(sensor.getId())) {
return sensor.getValue();
}
}
}
return tempCpub;
}
public int getFanRpm() {
if (fans != null) {
for (FreeboxSensor fan : fans) {
if ("fan0_speed".equals(fan.getId())) {
return fan.getValue();
}
}
}
return fanRpm;
}
public boolean isBoxAuthenticated() {
return boxAuthenticated;
}
public String getDiskStatus() {
return diskStatus;
}
public String getBoxFlavor() {
return boxFlavor;
}
public String getUserMainStorage() {
return userMainStorage;
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxSystemConfigResponse} is the Java class used to map the
* response of the system API
* https://dev.freebox.fr/sdk/os/system/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxSystemConfigResponse extends FreeboxResponse<FreeboxSystemConfig> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in system API response", this);
}
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxUPnPAVConfig} 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
*/
public class FreeboxUPnPAVConfig {
private Boolean enabled;
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxUPnPAVConfigResponse} is the Java class used to map the
* response of the UPnP AV configuration API
* https://dev.freebox.fr/sdk/os/upnpav/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxUPnPAVConfigResponse extends FreeboxResponse<FreeboxUPnPAVConfig> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in UPnP AV configuration API response", this);
}
if (getResult().isEnabled() == null) {
throw new FreeboxException("No UPnP AV status in response", this);
}
}
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxWifiGlobalConfig} 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
*/
public class FreeboxWifiGlobalConfig {
private Boolean enabled;
private String macFilterState;
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public String getMacFilterState() {
return macFilterState;
}
public void setMacFilterState(String macFilterState) {
this.macFilterState = macFilterState;
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxWifiGlobalConfigResponse} is the Java class used to map the
* response of the Wifi global configuration API
* https://dev.freebox.fr/sdk/os/wifi/# *
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxWifiGlobalConfigResponse extends FreeboxResponse<FreeboxWifiGlobalConfig> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in Wifi global configuration API response", this);
}
if (getResult().isEnabled() == null) {
throw new FreeboxException("No Wifi status in response", this);
}
}
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
/**
* The {@link FreeboxXdslStatus} is the Java class used to map the "XdslStatus"
* structure used by the response of the connection xDSL status API
* https://dev.freebox.fr/sdk/os/connection/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxXdslStatus {
public static class InternalXdslStatus {
private String status;
private String protocol;
private String modulation;
private long uptime;
}
private InternalXdslStatus status;
public String getStatus() {
return status.status;
}
public String getProtocol() {
return status.protocol;
}
public String getModulation() {
return status.modulation;
}
public long getUptime() {
return status.uptime;
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api.model;
import org.openhab.binding.freebox.internal.api.FreeboxException;
/**
* The {@link FreeboxXdslStatusResponse} is the Java class used to map the
* response of the connection xDSL status API
* https://dev.freebox.fr/sdk/os/connection/#
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxXdslStatusResponse extends FreeboxResponse<FreeboxXdslStatus> {
@Override
public void evaluate() throws FreeboxException {
super.evaluate();
if (getResult() == null) {
throw new FreeboxException("Missing result data in connection xDSL status API response", this);
}
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.config;
/**
* The {@link FreeboxAirPlayDeviceConfiguration} is responsible for holding
* configuration informations associated to a Freebox AirPlay Device thing type
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxAirPlayDeviceConfiguration {
public static final String NAME = "name";
public static final String PASSWORD = "password";
public static final String ACCEPT_ALL_MP3 = "acceptAllMp3";
public String name;
public String password;
public Boolean acceptAllMp3;
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.config;
/**
* The {@link FreeboxNetDeviceConfiguration} is responsible for holding
* configuration informations associated to a Freebox Network Device
* thing type
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxNetDeviceConfiguration {
public static final String MAC_ADDRESS = "macAddress";
public String macAddress;
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.config;
/**
* The {@link FreeboxNetInterfaceConfiguration} is responsible for holding
* configuration informations associated to a Freebox Network Interface
* thing type
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxNetInterfaceConfiguration {
public static final String IP_ADDRESS = "ipAddress";
public String ipAddress;
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.config;
/**
* The {@link FreeboxPhoneConfiguration} is responsible for holding
* configuration informations associated to a Freebox Phone thing type
*
* @author Laurent Garnier - Initial contribution
*/
public class FreeboxPhoneConfiguration {
public static final String REFRESH_PHONE_INTERVAL = "refreshPhoneInterval";
public static final String REFRESH_PHONE_CALLS_INTERVAL = "refreshPhoneCallsInterval";
public Integer refreshPhoneInterval;
public Integer refreshPhoneCallsInterval;
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.config;
/**
* The {@link FreeboxServerConfiguration} is responsible for holding
* configuration informations needed to access/poll the freebox server
*
* @author Gaël L'hopital - Initial contribution
* @author Laurent Garnier - add discovery settings
*/
public class FreeboxServerConfiguration {
public static final String FQDN = "fqdn";
public static final String APP_TOKEN = "appToken";
public static final String REFRESH_INTERVAL = "refreshInterval";
public static final String USE_ONLY_HTTP = "useOnlyHttp";
public static final String DISCOVER_PHONE = "discoverPhone";
public static final String DISCOVER_NET_DEVICE = "discoverNetDevice";
public static final String DISCOVER_NET_INTERFACE = "discoverNetInterface";
public static final String DISCOVER_AIRPLAY_RECEIVER = "discoverAirPlayReceiver";
public String fqdn;
public String appToken;
public Integer refreshInterval;
public Boolean useOnlyHttp;
public Boolean discoverPhone;
public Boolean discoverNetDevice;
public Boolean discoverNetInterface;
public Boolean discoverAirPlayReceiver;
}

View File

@@ -0,0 +1,98 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.console;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.freebox.internal.handler.FreeboxHandler;
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 FreeboxCommandExtension} is responsible for handling console commands
*
* @author Laurent Garnier - Initial contribution
*/
@NonNullByDefault
@Component(service = ConsoleCommandExtension.class)
public class FreeboxCommandExtension extends AbstractConsoleCommandExtension {
private static final String APP_TOKEN = "apptoken";
private final ThingRegistry thingRegistry;
@Activate
public FreeboxCommandExtension(final @Reference ThingRegistry thingRegistry) {
super("freebox", "Interact with the freebox 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;
FreeboxHandler handler = null;
if (thing != null) {
thingHandler = thing.getHandler();
if (thingHandler instanceof FreeboxHandler) {
handler = (FreeboxHandler) 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.getAppToken();
console.println("Your application token is " + (token != null ? token : "undefined"));
break;
default:
printUsage(console);
break;
}
}
} else {
printUsage(console);
}
}
@Override
public List<String> getUsages() {
return Arrays.asList(buildCommandUsage("<bridgeUID> " + APP_TOKEN, "show the application token"));
}
}

View File

@@ -0,0 +1,220 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.discovery;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.freebox.internal.FreeboxBindingConstants;
import org.openhab.binding.freebox.internal.FreeboxDataListener;
import org.openhab.binding.freebox.internal.api.FreeboxException;
import org.openhab.binding.freebox.internal.api.model.FreeboxAirMediaReceiver;
import org.openhab.binding.freebox.internal.api.model.FreeboxLanHost;
import org.openhab.binding.freebox.internal.api.model.FreeboxLanHostL3Connectivity;
import org.openhab.binding.freebox.internal.config.FreeboxAirPlayDeviceConfiguration;
import org.openhab.binding.freebox.internal.config.FreeboxNetDeviceConfiguration;
import org.openhab.binding.freebox.internal.config.FreeboxNetInterfaceConfiguration;
import org.openhab.binding.freebox.internal.config.FreeboxServerConfiguration;
import org.openhab.binding.freebox.internal.handler.FreeboxHandler;
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.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link FreeboxDiscoveryService} is responsible for discovering all things
* except the Freebox Server thing itself
*
* @author Laurent Garnier - Initial contribution
* @author Laurent Garnier - add discovery settings
* @author Laurent Garnier - use new internal API manager
*/
public class FreeboxDiscoveryService extends AbstractDiscoveryService implements FreeboxDataListener {
private final Logger logger = LoggerFactory.getLogger(FreeboxDiscoveryService.class);
private static final int SEARCH_TIME = 10;
private static final String PHONE_ID = "wired";
private FreeboxHandler bridgeHandler;
private boolean discoverPhone;
private boolean discoverNetDevice;
private boolean discoverNetInterface;
private boolean discoverAirPlayReceiver;
/**
* Creates a FreeboxDiscoveryService with background discovery disabled.
*/
public FreeboxDiscoveryService(FreeboxHandler freeboxBridgeHandler) {
super(FreeboxBindingConstants.SUPPORTED_THING_TYPES_UIDS, SEARCH_TIME, false);
this.bridgeHandler = freeboxBridgeHandler;
this.discoverPhone = true;
this.discoverNetDevice = true;
this.discoverNetInterface = true;
this.discoverAirPlayReceiver = true;
}
@Override
public void activate(@Nullable Map<@NonNull String, @Nullable Object> configProperties) {
super.activate(configProperties);
applyConfig(configProperties);
bridgeHandler.registerDataListener(this);
}
@Override
public void deactivate() {
bridgeHandler.unregisterDataListener(this);
super.deactivate();
}
@Override
public void applyConfig(Map<String, Object> configProperties) {
if (configProperties != null) {
Object property = configProperties.get(FreeboxServerConfiguration.DISCOVER_PHONE);
if (property != null) {
discoverPhone = ((Boolean) property).booleanValue();
}
property = configProperties.get(FreeboxServerConfiguration.DISCOVER_NET_DEVICE);
if (property != null) {
discoverNetDevice = ((Boolean) property).booleanValue();
}
property = configProperties.get(FreeboxServerConfiguration.DISCOVER_NET_INTERFACE);
if (property != null) {
discoverNetInterface = ((Boolean) property).booleanValue();
}
property = configProperties.get(FreeboxServerConfiguration.DISCOVER_AIRPLAY_RECEIVER);
if (property != null) {
discoverAirPlayReceiver = ((Boolean) property).booleanValue();
}
}
logger.debug("Freebox discovery - discoverPhone : {}", discoverPhone);
logger.debug("Freebox discovery - discoverNetDevice : {}", discoverNetDevice);
logger.debug("Freebox discovery - discoverNetInterface : {}", discoverNetInterface);
logger.debug("Freebox discovery - discoverAirPlayReceiver : {}", discoverAirPlayReceiver);
}
@Override
protected void startScan() {
logger.debug("Starting Freebox discovery scan");
if (bridgeHandler.getThing().getStatus() == ThingStatus.ONLINE) {
try {
List<FreeboxLanHost> lanHosts = bridgeHandler.getApiManager().getLanHosts();
List<FreeboxAirMediaReceiver> airPlayDevices = bridgeHandler.getApiManager().getAirMediaReceivers();
onDataFetched(bridgeHandler.getThing().getUID(), lanHosts, airPlayDevices);
} catch (FreeboxException e) {
logger.warn("Error while requesting data for things discovery", e);
}
}
}
@Override
public void onDataFetched(ThingUID bridge, List<FreeboxLanHost> lanHosts,
List<FreeboxAirMediaReceiver> airPlayDevices) {
if (bridge == null) {
return;
}
ThingUID thingUID;
DiscoveryResult discoveryResult;
if (discoverPhone) {
// Phone
thingUID = new ThingUID(FreeboxBindingConstants.FREEBOX_THING_TYPE_PHONE, bridge, PHONE_ID);
logger.trace("Adding new Freebox Phone {} to inbox", thingUID);
discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridge).withLabel("Wired phone")
.build();
thingDiscovered(discoveryResult);
}
if (lanHosts != null && (discoverNetDevice || discoverNetInterface)) {
// Network devices
for (FreeboxLanHost host : lanHosts) {
String mac = host.getMAC();
String primaryName = host.getPrimaryName();
String vendorName = host.getVendorName();
if (mac != null && !mac.isEmpty()) {
if (discoverNetDevice) {
String uid = mac.replaceAll("[^A-Za-z0-9_]", "_");
thingUID = new ThingUID(FreeboxBindingConstants.FREEBOX_THING_TYPE_NET_DEVICE, bridge, uid);
String name = (primaryName == null || primaryName.isEmpty()) ? ("Freebox Network Device " + mac)
: primaryName;
logger.trace("Adding new Freebox Network Device {} to inbox", thingUID);
Map<String, Object> properties = new HashMap<>(1);
if (vendorName != null && !vendorName.isEmpty()) {
properties.put(Thing.PROPERTY_VENDOR, vendorName);
}
properties.put(FreeboxNetDeviceConfiguration.MAC_ADDRESS, mac);
discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withBridge(bridge).withLabel(name).build();
thingDiscovered(discoveryResult);
}
// Network interfaces
if (host.getL3Connectivities() != null && discoverNetInterface) {
for (FreeboxLanHostL3Connectivity l3 : host.getL3Connectivities()) {
String addr = l3.getAddr();
if (addr != null && !addr.isEmpty()) {
String uid = addr.replaceAll("[^A-Za-z0-9_]", "_");
thingUID = new ThingUID(FreeboxBindingConstants.FREEBOX_THING_TYPE_NET_INTERFACE,
bridge, uid);
String name = addr;
if (primaryName != null && !primaryName.isEmpty()) {
name += " (" + (primaryName + ")");
}
logger.trace("Adding new Freebox Network Interface {} to inbox", thingUID);
Map<String, Object> properties = new HashMap<>(1);
if (vendorName != null && !vendorName.isEmpty()) {
properties.put(Thing.PROPERTY_VENDOR, vendorName);
}
properties.put(FreeboxNetInterfaceConfiguration.IP_ADDRESS, addr);
discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withBridge(bridge).withLabel(name).build();
thingDiscovered(discoveryResult);
}
}
}
}
}
}
if (airPlayDevices != null && discoverAirPlayReceiver) {
// AirPlay devices
for (FreeboxAirMediaReceiver device : airPlayDevices) {
String name = device.getName();
boolean videoCapable = device.isVideoCapable();
logger.debug("AirPlay Device name {} video capable {}", name, videoCapable);
// The Freebox API allows pushing media only to receivers with photo or video capabilities
// but not to receivers with only audio capability; so receivers without video capability
// are ignored by the discovery
if (name != null && !name.isEmpty() && videoCapable) {
String uid = name.replaceAll("[^A-Za-z0-9_]", "_");
thingUID = new ThingUID(FreeboxBindingConstants.FREEBOX_THING_TYPE_AIRPLAY, bridge, uid);
logger.trace("Adding new Freebox AirPlay Device {} to inbox", thingUID);
Map<String, Object> properties = new HashMap<>(1);
properties.put(FreeboxAirPlayDeviceConfiguration.NAME, name);
discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withBridge(bridge).withLabel(name + " (AirPlay)").build();
thingDiscovered(discoveryResult);
}
}
}
}
}

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.discovery;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.jmdns.ServiceInfo;
import org.openhab.binding.freebox.internal.FreeboxBindingConstants;
import org.openhab.binding.freebox.internal.config.FreeboxServerConfiguration;
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.Thing;
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 FreeboxServerDiscoveryParticipant} is responsible for discovering
* the Freebox Server (bridge) thing using mDNS discovery service
*
* @author Laurent Garnier - Initial contribution
*/
@Component(immediate = true)
public class FreeboxServerDiscoveryParticipant implements MDNSDiscoveryParticipant {
private final Logger logger = LoggerFactory.getLogger(FreeboxServerDiscoveryParticipant.class);
private static final String SERVICE_TYPE = "_fbx-api._tcp.local.";
@Override
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
return FreeboxBindingConstants.SUPPORTED_BRIDGE_TYPES_UIDS;
}
@Override
public String getServiceType() {
return SERVICE_TYPE;
}
@Override
public ThingUID getThingUID(ServiceInfo service) {
if ((service.getType() != null) && service.getType().equals(getServiceType())
&& (service.getPropertyString("uid") != null)) {
return new ThingUID(FreeboxBindingConstants.FREEBOX_BRIDGE_TYPE_SERVER,
service.getPropertyString("uid").replaceAll("[^A-Za-z0-9_]", "_"));
}
return null;
}
@Override
public DiscoveryResult createResult(ServiceInfo service) {
logger.debug("createResult ServiceInfo: {}", service);
DiscoveryResult result = null;
String ip = null;
if (service.getHostAddresses() != null && service.getHostAddresses().length > 0
&& !service.getHostAddresses()[0].isEmpty()) {
ip = service.getHostAddresses()[0];
}
ThingUID thingUID = getThingUID(service);
if (thingUID != null && ip != null) {
logger.info("Created a DiscoveryResult for Freebox Server {} on IP {}", thingUID, ip);
Map<String, Object> properties = new HashMap<>(1);
properties.put(FreeboxServerConfiguration.FQDN, ip + ":" + service.getPort());
properties.put(FreeboxServerConfiguration.USE_ONLY_HTTP, "true");
if (service.getPropertyString("device_type") != null) {
properties.put(Thing.PROPERTY_HARDWARE_VERSION, service.getPropertyString("device_type"));
}
if (service.getPropertyString("api_base_url") != null) {
properties.put(FreeboxBindingConstants.API_BASE_URL, service.getPropertyString("api_base_url"));
}
if (service.getPropertyString("api_version") != null) {
properties.put(FreeboxBindingConstants.API_VERSION, service.getPropertyString("api_version"));
}
result = DiscoveryResultBuilder.create(thingUID).withProperties(properties).withLabel(service.getName())
.build();
}
return result;
}
}

View File

@@ -0,0 +1,722 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.handler;
import static org.openhab.binding.freebox.internal.FreeboxBindingConstants.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.freebox.internal.FreeboxDataListener;
import org.openhab.binding.freebox.internal.api.FreeboxApiManager;
import org.openhab.binding.freebox.internal.api.FreeboxException;
import org.openhab.binding.freebox.internal.api.model.FreeboxAirMediaReceiver;
import org.openhab.binding.freebox.internal.api.model.FreeboxConnectionStatus;
import org.openhab.binding.freebox.internal.api.model.FreeboxDiscoveryResponse;
import org.openhab.binding.freebox.internal.api.model.FreeboxLanHost;
import org.openhab.binding.freebox.internal.api.model.FreeboxLcdConfig;
import org.openhab.binding.freebox.internal.api.model.FreeboxSambaConfig;
import org.openhab.binding.freebox.internal.api.model.FreeboxSystemConfig;
import org.openhab.binding.freebox.internal.config.FreeboxServerConfiguration;
import org.openhab.core.config.core.Configuration;
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.OpenClosedType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.types.UpDownType;
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.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link FreeboxHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Gaël L'hopital - Initial contribution
* @author Laurent Garnier - updated to a bridge handler and delegate few things to another handler
* @author Laurent Garnier - update discovery configuration
* @author Laurent Garnier - use new internal API manager
*/
public class FreeboxHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(FreeboxHandler.class);
private ScheduledFuture<?> authorizeJob;
private ScheduledFuture<?> globalJob;
private FreeboxApiManager apiManager;
private long uptime;
private List<FreeboxDataListener> dataListeners = new CopyOnWriteArrayList<>();
private FreeboxServerConfiguration configuration;
public FreeboxHandler(Bridge bridge) {
super(bridge);
Bundle bundle = FrameworkUtil.getBundle(getClass());
String appId = bundle.getSymbolicName();
String appName = bundle.getHeaders().get("Bundle-Name");
String appVersion = String.format("%d.%d", bundle.getVersion().getMajor(), bundle.getVersion().getMinor());
String deviceName = bundle.getHeaders().get("Bundle-Vendor");
this.apiManager = new FreeboxApiManager(appId, appName, appVersion, deviceName);
uptime = -1;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
return;
}
if (getThing().getStatus() == ThingStatus.UNKNOWN || (getThing().getStatus() == ThingStatus.OFFLINE
&& getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR)) {
return;
}
switch (channelUID.getId()) {
case LCDBRIGHTNESS:
setBrightness(channelUID, command);
break;
case LCDORIENTATION:
setOrientation(channelUID, command);
break;
case LCDFORCED:
setForced(channelUID, command);
break;
case WIFISTATUS:
setWifiStatus(channelUID, command);
break;
case FTPSTATUS:
setFtpStatus(channelUID, command);
break;
case AIRMEDIASTATUS:
setAirMediaStatus(channelUID, command);
break;
case UPNPAVSTATUS:
setUPnPAVStatus(channelUID, command);
break;
case SAMBAFILESTATUS:
setSambaFileStatus(channelUID, command);
break;
case SAMBAPRINTERSTATUS:
setSambaPrinterStatus(channelUID, command);
break;
case REBOOT:
reboot(channelUID, command);
break;
default:
logger.debug("Thing {}: unexpected command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
break;
}
}
@Override
public void initialize() {
logger.debug("initializing Freebox Server handler for thing {}", getThing().getUID());
configuration = getConfigAs(FreeboxServerConfiguration.class);
// Update the discovery configuration
Map<String, Object> configDiscovery = new HashMap<>();
configDiscovery.put(FreeboxServerConfiguration.DISCOVER_PHONE, configuration.discoverPhone);
configDiscovery.put(FreeboxServerConfiguration.DISCOVER_NET_DEVICE, configuration.discoverNetDevice);
configDiscovery.put(FreeboxServerConfiguration.DISCOVER_NET_INTERFACE, configuration.discoverNetInterface);
configDiscovery.put(FreeboxServerConfiguration.DISCOVER_AIRPLAY_RECEIVER,
configuration.discoverAirPlayReceiver);
for (FreeboxDataListener dataListener : dataListeners) {
dataListener.applyConfig(configDiscovery);
}
if (configuration.fqdn != null && !configuration.fqdn.isEmpty()) {
if (configuration.appToken == null || configuration.appToken.isEmpty()) {
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING,
"Please accept pairing request directly on your freebox");
} else {
updateStatus(ThingStatus.UNKNOWN);
}
logger.debug("Binding will schedule a job to establish a connection...");
if (authorizeJob == null || authorizeJob.isCancelled()) {
authorizeJob = scheduler.schedule(this::authorize, 1, TimeUnit.SECONDS);
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Freebox Server FQDN not set in the thing configuration");
}
}
private void pollServerState() {
logger.debug("Polling server state...");
boolean commOk = true;
commOk &= fetchSystemConfig();
commOk &= fetchLCDConfig();
commOk &= fetchWifiConfig();
commOk &= (fetchxDslStatus() || fetchFtthPresent());
commOk &= fetchConnectionStatus();
commOk &= fetchFtpConfig();
commOk &= fetchAirMediaConfig();
commOk &= fetchUPnPAVConfig();
commOk &= fetchSambaConfig();
List<FreeboxLanHost> lanHosts = fetchLanHosts();
commOk &= (lanHosts != null);
List<FreeboxAirMediaReceiver> airPlayDevices = fetchAirPlayDevices();
commOk &= (airPlayDevices != null);
// Trigger a new discovery of things
for (FreeboxDataListener dataListener : dataListeners) {
dataListener.onDataFetched(getThing().getUID(), lanHosts, airPlayDevices);
}
if (commOk) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
}
private void authorize() {
logger.debug("Authorize job...");
String fqdn = configuration.fqdn;
FreeboxDiscoveryResponse result = null;
boolean httpsRequestOk = false;
if (!Boolean.TRUE.equals(configuration.useOnlyHttp)) {
result = apiManager.checkApi(fqdn, true);
httpsRequestOk = (result != null);
}
if (!httpsRequestOk) {
result = apiManager.checkApi(fqdn, false);
}
String apiBaseUrl = result == null ? null : result.getApiBaseUrl();
String apiVersion = result == null ? null : result.getApiVersion();
String deviceType = result == null ? null : result.getDeviceType();
String apiDomain = result == null ? null : result.getApiDomain();
Integer httpsPort = result == null ? null : result.getHttpsPort();
boolean useHttps = false;
String errorMsg = null;
if (result == null) {
errorMsg = "Can't connect to " + fqdn;
} else if (apiBaseUrl == null || apiBaseUrl.isEmpty()) {
errorMsg = fqdn + " does not deliver any API base URL";
} else if (apiVersion == null || apiVersion.isEmpty()) {
errorMsg = fqdn + " does not deliver any API version";
} else if (Boolean.TRUE.equals(result.isHttpsAvailable()) && !Boolean.TRUE.equals(configuration.useOnlyHttp)) {
if (httpsPort == null || apiDomain == null || apiDomain.isEmpty()) {
if (httpsRequestOk) {
useHttps = true;
} else {
logger.debug("{} does not deliver API domain or HTTPS port; use HTTP API", fqdn);
}
} else if (apiManager.checkApi(String.format("%s:%d", apiDomain, httpsPort), true) != null) {
useHttps = true;
fqdn = String.format("%s:%d", apiDomain, httpsPort);
}
}
if (errorMsg != null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg);
} else if (!apiManager.authorize(useHttps, fqdn, apiBaseUrl, apiVersion, configuration.appToken)) {
if (configuration.appToken == null || configuration.appToken.isEmpty()) {
errorMsg = "Pairing request rejected or timeout";
} else {
errorMsg = "Check your app token in the thing configuration; opening session with " + fqdn + " using "
+ (useHttps ? "HTTPS" : "HTTP") + " API version " + apiVersion + " failed";
}
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg);
} else {
logger.debug("Thing {}: session opened with {} using {} API version {}", getThing().getUID(), fqdn,
(useHttps ? "HTTPS" : "HTTP"), apiVersion);
String appToken = apiManager.getAppToken();
if ((configuration.appToken == null || configuration.appToken.isEmpty()) && appToken != null) {
logger.debug("Store new app token in the thing configuration");
configuration.appToken = appToken;
Configuration thingConfig = editConfiguration();
thingConfig.put(FreeboxServerConfiguration.APP_TOKEN, appToken);
updateConfiguration(thingConfig);
}
updateStatus(ThingStatus.ONLINE);
if (globalJob == null || globalJob.isCancelled()) {
long pollingInterval = configuration.refreshInterval;
logger.debug("Scheduling server state update every {} seconds...", pollingInterval);
globalJob = scheduler.scheduleWithFixedDelay(() -> {
try {
pollServerState();
} catch (Exception e) {
logger.debug("Server state job failed: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}, 1, pollingInterval, TimeUnit.SECONDS);
}
}
Map<String, String> properties = editProperties();
if (apiBaseUrl != null && !apiBaseUrl.isEmpty()) {
properties.put(API_BASE_URL, apiBaseUrl);
}
if (apiVersion != null && !apiVersion.isEmpty()) {
properties.put(API_VERSION, apiVersion);
}
if (deviceType != null && !deviceType.isEmpty()) {
properties.put(Thing.PROPERTY_HARDWARE_VERSION, deviceType);
}
updateProperties(properties);
}
@Override
public void dispose() {
logger.debug("Disposing Freebox Server handler for thing {}", getThing().getUID());
if (authorizeJob != null && !authorizeJob.isCancelled()) {
authorizeJob.cancel(true);
authorizeJob = null;
}
if (globalJob != null && !globalJob.isCancelled()) {
globalJob.cancel(true);
globalJob = null;
}
apiManager.closeSession();
super.dispose();
}
public FreeboxApiManager getApiManager() {
return apiManager;
}
public String getAppToken() {
return configuration == null ? null : configuration.appToken;
}
public boolean registerDataListener(FreeboxDataListener dataListener) {
if (dataListener == null) {
throw new IllegalArgumentException("It's not allowed to pass a null dataListener.");
}
return dataListeners.add(dataListener);
}
public boolean unregisterDataListener(FreeboxDataListener dataListener) {
if (dataListener == null) {
throw new IllegalArgumentException("It's not allowed to pass a null dataListener.");
}
return dataListeners.remove(dataListener);
}
private boolean fetchConnectionStatus() {
try {
FreeboxConnectionStatus connectionStatus = apiManager.getConnectionStatus();
String state = connectionStatus.getState();
if (state != null && !state.isEmpty()) {
updateChannelStringState(LINESTATUS, state);
}
String ipv4 = connectionStatus.getIpv4();
if (ipv4 != null && !ipv4.isEmpty()) {
updateChannelStringState(IPV4, ipv4);
}
updateChannelDecimalState(RATEUP, connectionStatus.getRateUp());
updateChannelDecimalState(RATEDOWN, connectionStatus.getRateDown());
updateChannelDecimalState(BYTESUP, connectionStatus.getBytesUp());
updateChannelDecimalState(BYTESDOWN, connectionStatus.getBytesDown());
return true;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchConnectionStatus: {}", getThing().getUID(), e.getMessage(), e);
return false;
}
}
private boolean fetchxDslStatus() {
try {
String status = apiManager.getxDslStatus();
if (status != null && !status.isEmpty()) {
updateChannelStringState(XDSLSTATUS, status);
}
return true;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchxDslStatus: {}", getThing().getUID(), e.getMessage(), e);
return false;
}
}
private boolean fetchFtthPresent() {
try {
boolean status = apiManager.getFtthPresent();
updateChannelSwitchState(FTTHSTATUS, status);
return status;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchxFtthStatus: {}", getThing().getUID(), e.getMessage(), e);
return false;
}
}
private boolean fetchWifiConfig() {
try {
updateChannelSwitchState(WIFISTATUS, apiManager.isWifiEnabled());
return true;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchWifiConfig: {}", getThing().getUID(), e.getMessage(), e);
return false;
}
}
private boolean fetchFtpConfig() {
try {
updateChannelSwitchState(FTPSTATUS, apiManager.isFtpEnabled());
return true;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchFtpConfig: {}", getThing().getUID(), e.getMessage(), e);
return false;
}
}
private boolean fetchAirMediaConfig() {
try {
if (!apiManager.isInLanBridgeMode()) {
updateChannelSwitchState(AIRMEDIASTATUS, apiManager.isAirMediaEnabled());
}
return true;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchAirMediaConfig: {}", getThing().getUID(), e.getMessage(), e);
return false;
}
}
private boolean fetchUPnPAVConfig() {
try {
if (!apiManager.isInLanBridgeMode()) {
updateChannelSwitchState(UPNPAVSTATUS, apiManager.isUPnPAVEnabled());
}
return true;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchUPnPAVConfig: {}", getThing().getUID(), e.getMessage(), e);
return false;
}
}
private boolean fetchSambaConfig() {
try {
FreeboxSambaConfig config = apiManager.getSambaConfig();
updateChannelSwitchState(SAMBAFILESTATUS, config.isFileShareEnabled());
updateChannelSwitchState(SAMBAPRINTERSTATUS, config.isPrintShareEnabled());
return true;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchSambaConfig: {}", getThing().getUID(), e.getMessage(), e);
return false;
}
}
private boolean fetchLCDConfig() {
try {
FreeboxLcdConfig config = apiManager.getLcdConfig();
updateChannelDecimalState(LCDBRIGHTNESS, config.getBrightness());
updateChannelDecimalState(LCDORIENTATION, config.getOrientation());
updateChannelSwitchState(LCDFORCED, config.isOrientationForced());
return true;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchLCDConfig: {}", getThing().getUID(), e.getMessage(), e);
return false;
}
}
private boolean fetchSystemConfig() {
try {
FreeboxSystemConfig config = apiManager.getSystemConfig();
Map<String, String> properties = editProperties();
String value = config.getSerial();
if (value != null && !value.isEmpty()) {
properties.put(Thing.PROPERTY_SERIAL_NUMBER, value);
}
value = config.getBoardName();
if (value != null && !value.isEmpty()) {
properties.put(Thing.PROPERTY_HARDWARE_VERSION, value);
}
value = config.getFirmwareVersion();
if (value != null && !value.isEmpty()) {
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, value);
updateChannelStringState(FWVERSION, value);
}
value = config.getMac();
if (value != null && !value.isEmpty()) {
properties.put(Thing.PROPERTY_MAC_ADDRESS, value);
}
updateProperties(properties);
long newUptime = config.getUptimeVal();
updateChannelSwitchState(RESTARTED, newUptime < uptime);
uptime = newUptime;
updateChannelDecimalState(UPTIME, uptime);
updateChannelDecimalState(TEMPCPUM, config.getTempCpum());
updateChannelDecimalState(TEMPCPUB, config.getTempCpub());
updateChannelDecimalState(TEMPSWITCH, config.getTempSw());
updateChannelDecimalState(FANSPEED, config.getFanRpm());
return true;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchSystemConfig: {}", getThing().getUID(), e.getMessage(), e);
return false;
}
}
private synchronized List<FreeboxLanHost> fetchLanHosts() {
try {
List<FreeboxLanHost> hosts = apiManager.getLanHosts();
if (hosts == null) {
hosts = new ArrayList<>();
}
// The update of channels is delegated to each thing handler
for (Thing thing : getThing().getThings()) {
ThingHandler handler = thing.getHandler();
if (handler instanceof FreeboxThingHandler) {
((FreeboxThingHandler) handler).updateNetInfo(hosts);
}
}
return hosts;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchLanHosts: {}", getThing().getUID(), e.getMessage(), e);
return null;
}
}
private synchronized List<FreeboxAirMediaReceiver> fetchAirPlayDevices() {
try {
List<FreeboxAirMediaReceiver> devices = apiManager.getAirMediaReceivers();
if (devices == null) {
devices = new ArrayList<>();
}
// The update of channels is delegated to each thing handler
for (Thing thing : getThing().getThings()) {
ThingHandler handler = thing.getHandler();
if (handler instanceof FreeboxThingHandler) {
((FreeboxThingHandler) handler).updateAirPlayDevice(devices);
}
}
return devices;
} catch (FreeboxException e) {
logger.debug("Thing {}: exception in fetchAirPlayDevices: {}", getThing().getUID(), e.getMessage(), e);
return null;
}
}
private void setBrightness(ChannelUID channelUID, Command command) {
try {
if (command instanceof IncreaseDecreaseType) {
if (command == IncreaseDecreaseType.INCREASE) {
updateChannelDecimalState(LCDBRIGHTNESS, apiManager.increaseLcdBrightness());
} else {
updateChannelDecimalState(LCDBRIGHTNESS, apiManager.decreaseLcdBrightness());
}
} else if (command instanceof OnOffType) {
updateChannelDecimalState(LCDBRIGHTNESS,
apiManager.setLcdBrightness((command == OnOffType.ON) ? 100 : 0));
} else if (command instanceof DecimalType) {
updateChannelDecimalState(LCDBRIGHTNESS,
apiManager.setLcdBrightness(((DecimalType) command).intValue()));
} else if (command instanceof PercentType) {
updateChannelDecimalState(LCDBRIGHTNESS,
apiManager.setLcdBrightness(((PercentType) command).intValue()));
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
} catch (FreeboxException e) {
logCommandException(e, channelUID, command);
fetchLCDConfig();
}
}
private void setOrientation(ChannelUID channelUID, Command command) {
if (command instanceof DecimalType) {
try {
FreeboxLcdConfig config = apiManager.setLcdOrientation(((DecimalType) command).intValue());
updateChannelDecimalState(LCDORIENTATION, config.getOrientation());
updateChannelSwitchState(LCDFORCED, config.isOrientationForced());
} catch (FreeboxException e) {
logCommandException(e, channelUID, command);
fetchLCDConfig();
}
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
}
private void setForced(ChannelUID channelUID, Command command) {
if (command instanceof OnOffType || command instanceof OpenClosedType || command instanceof UpDownType) {
try {
updateChannelSwitchState(LCDFORCED, apiManager.setLcdOrientationForced(command.equals(OnOffType.ON)
|| command.equals(UpDownType.UP) || command.equals(OpenClosedType.OPEN)));
} catch (FreeboxException e) {
logCommandException(e, channelUID, command);
fetchLCDConfig();
}
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
}
private void setWifiStatus(ChannelUID channelUID, Command command) {
if (command instanceof OnOffType || command instanceof OpenClosedType || command instanceof UpDownType) {
try {
updateChannelSwitchState(WIFISTATUS, apiManager.enableWifi(command.equals(OnOffType.ON)
|| command.equals(UpDownType.UP) || command.equals(OpenClosedType.OPEN)));
} catch (FreeboxException e) {
logCommandException(e, channelUID, command);
fetchWifiConfig();
}
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
}
private void setFtpStatus(ChannelUID channelUID, Command command) {
if (command instanceof OnOffType || command instanceof OpenClosedType || command instanceof UpDownType) {
try {
updateChannelSwitchState(FTPSTATUS, apiManager.enableFtp(command.equals(OnOffType.ON)
|| command.equals(UpDownType.UP) || command.equals(OpenClosedType.OPEN)));
} catch (FreeboxException e) {
logCommandException(e, channelUID, command);
fetchFtpConfig();
}
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
}
private void setAirMediaStatus(ChannelUID channelUID, Command command) {
if (command instanceof OnOffType || command instanceof OpenClosedType || command instanceof UpDownType) {
try {
if (!apiManager.isInLanBridgeMode()) {
updateChannelSwitchState(AIRMEDIASTATUS, apiManager.enableAirMedia(command.equals(OnOffType.ON)
|| command.equals(UpDownType.UP) || command.equals(OpenClosedType.OPEN)));
} else {
logger.debug("Thing {}: command {} from channel {} unavailable when in bridge mode",
getThing().getUID(), command, channelUID.getId());
}
} catch (FreeboxException e) {
logCommandException(e, channelUID, command);
fetchAirMediaConfig();
}
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
}
private void setUPnPAVStatus(ChannelUID channelUID, Command command) {
if (command instanceof OnOffType || command instanceof OpenClosedType || command instanceof UpDownType) {
try {
if (!apiManager.isInLanBridgeMode()) {
updateChannelSwitchState(UPNPAVSTATUS, apiManager.enableUPnPAV(command.equals(OnOffType.ON)
|| command.equals(UpDownType.UP) || command.equals(OpenClosedType.OPEN)));
} else {
logger.debug("Thing {}: command {} from channel {} unavailable when in bridge mode",
getThing().getUID(), command, channelUID.getId());
}
} catch (FreeboxException e) {
logCommandException(e, channelUID, command);
fetchUPnPAVConfig();
}
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
}
private void setSambaFileStatus(ChannelUID channelUID, Command command) {
if (command instanceof OnOffType || command instanceof OpenClosedType || command instanceof UpDownType) {
try {
updateChannelSwitchState(SAMBAFILESTATUS, apiManager.enableSambaFileShare(command.equals(OnOffType.ON)
|| command.equals(UpDownType.UP) || command.equals(OpenClosedType.OPEN)));
} catch (FreeboxException e) {
logCommandException(e, channelUID, command);
fetchSambaConfig();
}
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
}
private void setSambaPrinterStatus(ChannelUID channelUID, Command command) {
if (command instanceof OnOffType || command instanceof OpenClosedType || command instanceof UpDownType) {
try {
updateChannelSwitchState(SAMBAPRINTERSTATUS,
apiManager.enableSambaPrintShare(command.equals(OnOffType.ON) || command.equals(UpDownType.UP)
|| command.equals(OpenClosedType.OPEN)));
} catch (FreeboxException e) {
logCommandException(e, channelUID, command);
fetchSambaConfig();
}
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
}
private void reboot(ChannelUID channelUID, Command command) {
if (command.equals(OnOffType.ON) || command.equals(UpDownType.UP) || command.equals(OpenClosedType.OPEN)) {
try {
apiManager.reboot();
} catch (FreeboxException e) {
logCommandException(e, channelUID, command);
}
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
}
private void updateChannelStringState(String channel, String state) {
updateState(new ChannelUID(getThing().getUID(), channel), new StringType(state));
}
private void updateChannelSwitchState(String channel, boolean state) {
updateState(new ChannelUID(getThing().getUID(), channel), state ? OnOffType.ON : OnOffType.OFF);
}
private void updateChannelDecimalState(String channel, int state) {
updateState(new ChannelUID(getThing().getUID(), channel), new DecimalType(state));
}
private void updateChannelDecimalState(String channel, long state) {
updateState(new ChannelUID(getThing().getUID(), channel), new DecimalType(state));
}
public void logCommandException(FreeboxException e, ChannelUID channelUID, Command command) {
if (e.isMissingRights()) {
logger.debug("Thing {}: missing right {} while handling command {} from channel {}", getThing().getUID(),
e.getResponse().getMissingRight(), command, channelUID.getId());
} else {
logger.debug("Thing {}: error while handling command {} from channel {}", getThing().getUID(), command,
channelUID.getId(), e);
}
}
}

View File

@@ -0,0 +1,425 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.handler;
import static org.openhab.binding.freebox.internal.FreeboxBindingConstants.*;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.freebox.internal.api.FreeboxException;
import org.openhab.binding.freebox.internal.api.model.FreeboxAirMediaReceiver;
import org.openhab.binding.freebox.internal.api.model.FreeboxCallEntry;
import org.openhab.binding.freebox.internal.api.model.FreeboxLanHost;
import org.openhab.binding.freebox.internal.api.model.FreeboxLanHostL3Connectivity;
import org.openhab.binding.freebox.internal.api.model.FreeboxPhoneStatus;
import org.openhab.binding.freebox.internal.config.FreeboxAirPlayDeviceConfiguration;
import org.openhab.binding.freebox.internal.config.FreeboxNetDeviceConfiguration;
import org.openhab.binding.freebox.internal.config.FreeboxNetInterfaceConfiguration;
import org.openhab.binding.freebox.internal.config.FreeboxPhoneConfiguration;
import org.openhab.core.i18n.TimeZoneProvider;
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.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.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link FreeboxThingHandler} is responsible for handling everything associated to
* any Freebox thing types except the bridge thing type.
*
* @author Laurent Garnier - Initial contribution
* @author Laurent Garnier - use new internal API manager
*/
public class FreeboxThingHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(FreeboxThingHandler.class);
private final TimeZoneProvider timeZoneProvider;
private ScheduledFuture<?> phoneJob;
private ScheduledFuture<?> callsJob;
private FreeboxHandler bridgeHandler;
private Calendar lastPhoneCheck;
private String netAddress;
private String airPlayName;
private String airPlayPassword;
public FreeboxThingHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
super(thing);
this.timeZoneProvider = timeZoneProvider;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
return;
}
if (getThing().getStatus() == ThingStatus.UNKNOWN || (getThing().getStatus() == ThingStatus.OFFLINE
&& (getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_OFFLINE
|| getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_UNINITIALIZED
|| getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR))) {
return;
}
if (bridgeHandler == null) {
return;
}
switch (channelUID.getId()) {
case PLAYURL:
playMedia(channelUID, command);
break;
case STOP:
stopMedia(channelUID, command);
break;
default:
logger.debug("Thing {}: unexpected command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
break;
}
}
@Override
public void initialize() {
logger.debug("initializing handler for thing {}", getThing().getUID());
Bridge bridge = getBridge();
if (bridge == null) {
initializeThing(null, null);
} else {
initializeThing(bridge.getHandler(), bridge.getStatus());
}
}
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
logger.debug("bridgeStatusChanged {}", bridgeStatusInfo);
Bridge bridge = getBridge();
if (bridge == null) {
initializeThing(null, bridgeStatusInfo.getStatus());
} else {
initializeThing(bridge.getHandler(), bridgeStatusInfo.getStatus());
}
}
private void initializeThing(ThingHandler bridgeHandler, ThingStatus bridgeStatus) {
if (bridgeHandler != null && bridgeStatus != null) {
if (bridgeStatus == ThingStatus.ONLINE) {
this.bridgeHandler = (FreeboxHandler) bridgeHandler;
if (getThing().getThingTypeUID().equals(FREEBOX_THING_TYPE_PHONE)) {
updateStatus(ThingStatus.ONLINE);
lastPhoneCheck = Calendar.getInstance();
if (phoneJob == null || phoneJob.isCancelled()) {
long pollingInterval = getConfigAs(FreeboxPhoneConfiguration.class).refreshPhoneInterval;
if (pollingInterval > 0) {
logger.debug("Scheduling phone state job every {} seconds...", pollingInterval);
phoneJob = scheduler.scheduleWithFixedDelay(() -> {
try {
pollPhoneState();
} catch (Exception e) {
logger.debug("Phone state job failed: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
e.getMessage());
}
}, 1, pollingInterval, TimeUnit.SECONDS);
}
}
if (callsJob == null || callsJob.isCancelled()) {
long pollingInterval = getConfigAs(FreeboxPhoneConfiguration.class).refreshPhoneCallsInterval;
if (pollingInterval > 0) {
logger.debug("Scheduling phone calls job every {} seconds...", pollingInterval);
callsJob = scheduler.scheduleWithFixedDelay(() -> {
try {
pollPhoneCalls();
} catch (Exception e) {
logger.debug("Phone calls job failed: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
e.getMessage());
}
}, 1, pollingInterval, TimeUnit.SECONDS);
}
}
} else if (getThing().getThingTypeUID().equals(FREEBOX_THING_TYPE_NET_DEVICE)) {
updateStatus(ThingStatus.ONLINE);
netAddress = getConfigAs(FreeboxNetDeviceConfiguration.class).macAddress;
netAddress = (netAddress == null) ? "" : netAddress;
} else if (getThing().getThingTypeUID().equals(FREEBOX_THING_TYPE_NET_INTERFACE)) {
updateStatus(ThingStatus.ONLINE);
netAddress = getConfigAs(FreeboxNetInterfaceConfiguration.class).ipAddress;
netAddress = (netAddress == null) ? "" : netAddress;
} else if (getThing().getThingTypeUID().equals(FREEBOX_THING_TYPE_AIRPLAY)) {
updateStatus(ThingStatus.UNKNOWN);
airPlayName = getConfigAs(FreeboxAirPlayDeviceConfiguration.class).name;
airPlayName = (airPlayName == null) ? "" : airPlayName;
airPlayPassword = getConfigAs(FreeboxAirPlayDeviceConfiguration.class).password;
airPlayPassword = (airPlayPassword == null) ? "" : airPlayPassword;
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
}
}
private void pollPhoneState() {
logger.debug("Polling phone state...");
try {
FreeboxPhoneStatus phoneStatus = bridgeHandler.getApiManager().getPhoneStatus();
updateGroupChannelSwitchState(STATE, ONHOOK, phoneStatus.isOnHook());
updateGroupChannelSwitchState(STATE, RINGING, phoneStatus.isRinging());
updateStatus(ThingStatus.ONLINE);
} catch (FreeboxException e) {
if (e.isMissingRights()) {
logger.debug("Phone state job: missing right {}", e.getResponse().getMissingRight());
updateStatus(ThingStatus.ONLINE);
} else {
logger.debug("Phone state job failed: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
}
private void pollPhoneCalls() {
logger.debug("Polling phone calls...");
try {
List<FreeboxCallEntry> callEntries = bridgeHandler.getApiManager().getCallEntries();
if (callEntries != null) {
PhoneCallComparator comparator = new PhoneCallComparator();
Collections.sort(callEntries, comparator);
for (FreeboxCallEntry call : callEntries) {
Calendar callEndTime = call.getTimeStamp();
callEndTime.add(Calendar.SECOND, call.getDuration());
if ((call.getDuration() > 0) && callEndTime.after(lastPhoneCheck)) {
updateCall(call, ANY);
if (call.isAccepted()) {
updateCall(call, ACCEPTED);
} else if (call.isMissed()) {
updateCall(call, MISSED);
} else if (call.isOutGoing()) {
updateCall(call, OUTGOING);
}
lastPhoneCheck = callEndTime;
}
}
}
updateStatus(ThingStatus.ONLINE);
} catch (FreeboxException e) {
if (e.isMissingRights()) {
logger.debug("Phone calls job: missing right {}", e.getResponse().getMissingRight());
updateStatus(ThingStatus.ONLINE);
} else {
logger.debug("Phone calls job failed: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
}
@Override
public void dispose() {
logger.debug("Disposing handler for thing {}", getThing().getUID());
if (phoneJob != null && !phoneJob.isCancelled()) {
phoneJob.cancel(true);
phoneJob = null;
}
if (callsJob != null && !callsJob.isCancelled()) {
callsJob.cancel(true);
callsJob = null;
}
super.dispose();
}
private void updateCall(FreeboxCallEntry call, String channelGroup) {
if (channelGroup != null) {
updateGroupChannelStringState(channelGroup, CALLNUMBER, call.getNumber());
updateGroupChannelDecimalState(channelGroup, CALLDURATION, call.getDuration());
ZonedDateTime zoned = ZonedDateTime.ofInstant(Instant.ofEpochMilli(call.getTimeStamp().getTimeInMillis()),
timeZoneProvider.getTimeZone());
updateGroupChannelDateTimeState(channelGroup, CALLTIMESTAMP, zoned);
updateGroupChannelStringState(channelGroup, CALLNAME, call.getName());
if (channelGroup.equals(ANY)) {
updateGroupChannelStringState(channelGroup, CALLSTATUS, call.getType());
}
}
}
public void updateNetInfo(List<FreeboxLanHost> hosts) {
if (!getThing().getThingTypeUID().equals(FREEBOX_THING_TYPE_NET_DEVICE)
&& !getThing().getThingTypeUID().equals(FREEBOX_THING_TYPE_NET_INTERFACE)) {
return;
}
if (getThing().getStatus() != ThingStatus.ONLINE) {
return;
}
boolean found = false;
boolean reachable = false;
String vendor = null;
if (hosts != null) {
for (FreeboxLanHost host : hosts) {
if (getThing().getThingTypeUID().equals(FREEBOX_THING_TYPE_NET_DEVICE)
&& netAddress.equals(host.getMAC())) {
found = true;
reachable = host.isReachable();
vendor = host.getVendorName();
break;
}
if (host.getL3Connectivities() != null) {
for (FreeboxLanHostL3Connectivity l3 : host.getL3Connectivities()) {
if (getThing().getThingTypeUID().equals(FREEBOX_THING_TYPE_NET_INTERFACE)
&& netAddress.equals(l3.getAddr())) {
found = true;
if (l3.isReachable()) {
reachable = true;
vendor = host.getVendorName();
break;
}
}
}
}
}
}
if (found) {
updateState(new ChannelUID(getThing().getUID(), REACHABLE), reachable ? OnOffType.ON : OnOffType.OFF);
}
if (vendor != null && !vendor.isEmpty()) {
updateProperty(Thing.PROPERTY_VENDOR, vendor);
}
}
public void updateAirPlayDevice(List<FreeboxAirMediaReceiver> receivers) {
if (!getThing().getThingTypeUID().equals(FREEBOX_THING_TYPE_AIRPLAY)) {
return;
}
if (airPlayName == null) {
return;
}
// The Freebox API allows pushing media only to receivers with photo or video capabilities
// but not to receivers with only audio capability
boolean found = false;
boolean usable = false;
if (receivers != null) {
for (FreeboxAirMediaReceiver receiver : receivers) {
if (airPlayName.equals(receiver.getName())) {
found = true;
usable = receiver.isVideoCapable();
break;
}
}
}
if (!found) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "AirPlay device not found");
} else if (!usable) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"AirPlay device without video capability");
} else {
updateStatus(ThingStatus.ONLINE);
}
}
public void playMedia(String url) throws FreeboxException {
if (bridgeHandler != null && url != null) {
stopMedia();
bridgeHandler.getApiManager().playMedia(url, airPlayName, airPlayPassword);
}
}
private void playMedia(ChannelUID channelUID, Command command) {
if (command instanceof StringType) {
try {
playMedia(command.toString());
} catch (FreeboxException e) {
bridgeHandler.logCommandException(e, channelUID, command);
}
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
}
public void stopMedia() throws FreeboxException {
if (bridgeHandler != null) {
bridgeHandler.getApiManager().stopMedia(airPlayName, airPlayPassword);
}
}
private void stopMedia(ChannelUID channelUID, Command command) {
if (command instanceof OnOffType) {
try {
stopMedia();
} catch (FreeboxException e) {
bridgeHandler.logCommandException(e, channelUID, command);
}
} else {
logger.debug("Thing {}: invalid command {} from channel {}", getThing().getUID(), command,
channelUID.getId());
}
}
private void updateGroupChannelSwitchState(String group, String channel, boolean state) {
updateState(new ChannelUID(getThing().getUID(), group, channel), state ? OnOffType.ON : OnOffType.OFF);
}
private void updateGroupChannelStringState(String group, String channel, String state) {
updateState(new ChannelUID(getThing().getUID(), group, channel), new StringType(state));
}
private void updateGroupChannelDecimalState(String group, String channel, int state) {
updateState(new ChannelUID(getThing().getUID(), group, channel), new DecimalType(state));
}
private void updateGroupChannelDateTimeState(String group, String channel, ZonedDateTime zonedDateTime) {
updateState(new ChannelUID(getThing().getUID(), group, channel), new DateTimeType(zonedDateTime));
}
/**
* A comparator of phone calls by ascending end date and time
*/
private class PhoneCallComparator implements Comparator<FreeboxCallEntry> {
@Override
public int compare(FreeboxCallEntry call1, FreeboxCallEntry call2) {
int result = 0;
Calendar callEndTime1 = call1.getTimeStamp();
callEndTime1.add(Calendar.SECOND, call1.getDuration());
Calendar callEndTime2 = call2.getTimeStamp();
callEndTime2.add(Calendar.SECOND, call2.getDuration());
if (callEndTime1.before(callEndTime2)) {
result = -1;
} else if (callEndTime1.after(callEndTime2)) {
result = 1;
}
return result;
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="freebox" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>Freebox Binding</name>
<description>The Freebox binding requests your Freebox Server for various operational informations.</description>
<author>Gaël L'hopital</author>
</binding:binding>

View File

@@ -0,0 +1,571 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="freebox"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<bridge-type id="server">
<label>Freebox Server</label>
<description>Provides various informations regarding the status of the Freebox Server</description>
<channels>
<channel id="fwversion" typeId="fwversion"/>
<channel id="uptime" typeId="uptime"/>
<channel id="restarted" typeId="restarted"/>
<channel id="tempcpum" typeId="tempcpum"/>
<channel id="tempcpub" typeId="tempcpub"/>
<channel id="tempswitch" typeId="tempswitch"/>
<channel id="fanspeed" typeId="fanspeed"/>
<channel id="reboot" typeId="reboot"/>
<channel id="lcd_brightness" typeId="lcd_brightness"/>
<channel id="lcd_orientation" typeId="lcd_orientation"/>
<channel id="lcd_forced" typeId="lcd_forced"/>
<channel id="wifi_status" typeId="wifi_status"/>
<channel id="ftp_status" typeId="ftp_status"/>
<channel id="airmedia_status" typeId="airmedia_status"/>
<channel id="upnpav_status" typeId="upnpav_status"/>
<channel id="sambafileshare_status" typeId="sambafileshare_status"/>
<channel id="sambaprintershare_status" typeId="sambaprintershare_status"/>
<channel id="xdsl_status" typeId="xdsl_status"/>
<channel id="ftth_status" typeId="ftth_status"/>
<channel id="line_status" typeId="line_status"/>
<channel id="ipv4" typeId="ipv4"/>
<channel id="rate_up" typeId="rate_up"/>
<channel id="rate_down" typeId="rate_down"/>
<channel id="bytes_up" typeId="bytes_up"/>
<channel id="bytes_down" typeId="bytes_down"/>
</channels>
<properties>
<property name="vendor">Freebox SAS</property>
</properties>
<config-description>
<parameter name="fqdn" type="text">
<label>Freebox Server FQDN</label>
<context>network-address</context>
<description>The IP address / FQDN of the Freebox Server (can include port number)</description>
<default>mafreebox.freebox.fr</default>
<required>false</required>
</parameter>
<parameter name="appToken" type="text">
<label>Application Token</label>
<context>password</context>
<description>Token generated by the Freebox server</description>
<required>false</required>
</parameter>
<parameter name="refreshInterval" type="integer" min="1" unit="s">
<label>Refresh Interval</label>
<description>The refresh interval in seconds which is used to poll given Freebox Server</description>
<default>30</default>
<required>false</required>
</parameter>
<parameter name="useOnlyHttp" type="boolean">
<label>Use Only HTTP API</label>
<description>Use HTTP API even if HTTPS is available</description>
<default>false</default>
<required>false</required>
<advanced>true</advanced>
</parameter>
<parameter name="discoverPhone" type="boolean">
<label>Enable Phone Discovery</label>
<description>Enable the discovery of phone things</description>
<default>true</default>
<required>false</required>
<advanced>true</advanced>
</parameter>
<parameter name="discoverNetDevice" type="boolean">
<label>Enable Network Device Discovery</label>
<description>Enable the discovery of network device things</description>
<default>true</default>
<required>false</required>
<advanced>true</advanced>
</parameter>
<parameter name="discoverNetInterface" type="boolean">
<label>Enable Network Interface Discovery</label>
<description>Enable the discovery of network interface things</description>
<default>true</default>
<required>false</required>
<advanced>true</advanced>
</parameter>
<parameter name="discoverAirPlayReceiver" type="boolean">
<label>Enable AirPlay Receiver Discovery</label>
<description>Enable the discovery of AirPlay receiver things</description>
<default>true</default>
<required>false</required>
<advanced>true</advanced>
</parameter>
</config-description>
</bridge-type>
<thing-type id="phone">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>Phone</label>
<description>Provides various informations regarding the phone state and the phone calls</description>
<channel-groups>
<channel-group id="state" typeId="state"/>
<channel-group id="any" typeId="any"/>
<channel-group id="accepted" typeId="accepted"/>
<channel-group id="missed" typeId="missed"/>
<channel-group id="outgoing" typeId="outgoing"/>
</channel-groups>
<config-description>
<parameter name="refreshPhoneInterval" type="integer" min="0" unit="s">
<label>Phone State Refresh Interval</label>
<description>The refresh interval in seconds which is used to poll given Freebox Server for phone state</description>
<default>2</default>
<required>false</required>
</parameter>
<parameter name="refreshPhoneCallsInterval" type="integer" min="0" unit="s">
<label>Phone Calls Refresh Interval</label>
<description>The refresh interval in seconds which is used to poll given Freebox Server for phone calls</description>
<default>60</default>
<required>false</required>
</parameter>
</config-description>
</thing-type>
<thing-type id="net_device">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>Network Device</label>
<description>Provides network device reachability</description>
<channels>
<channel id="reachable" typeId="reachable"/>
</channels>
<config-description>
<parameter name="macAddress" type="text">
<label>MAC Address</label>
<description>The MAC address of the network device</description>
<default></default>
<required>true</required>
</parameter>
</config-description>
</thing-type>
<thing-type id="net_interface">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>Network Interface</label>
<description>Provides network interface reachability</description>
<channels>
<channel id="reachable" typeId="reachable"/>
</channels>
<config-description>
<parameter name="ipAddress" type="text">
<label>IP Address</label>
<description>The IP address (v4 or v6) of the network interface</description>
<default></default>
<required>true</required>
</parameter>
</config-description>
</thing-type>
<thing-type id="airplay">
<supported-bridge-type-refs>
<bridge-type-ref id="server"/>
</supported-bridge-type-refs>
<label>AirPlay Receiver</label>
<description>Provides media control from one AirPlay device</description>
<channels>
<channel id="playurl" typeId="playurl"/>
<channel id="stop" typeId="stop"/>
</channels>
<config-description>
<parameter name="name" type="text">
<label>Name</label>
<description>Name of the AirPlay device</description>
<required>true</required>
</parameter>
<parameter name="password" type="text">
<context>password</context>
<label>Password</label>
<description>AirPlay password</description>
<default></default>
<required>false</required>
</parameter>
<parameter name="acceptAllMp3" type="boolean">
<label>Accept All MP3</label>
<description>Accept any bitrate for MP3 audio or only bitrates greater than 64 kbps</description>
<default>true</default>
<required>false</required>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>
<channel-group-type id="state">
<label>Phone</label>
<description>The phone state</description>
<channels>
<channel id="onhook" typeId="onhook"/>
<channel id="ringing" typeId="ringing"/>
</channels>
</channel-group-type>
<channel-group-type id="any">
<label>Call</label>
<description>The last phone call</description>
<channels>
<channel id="call_number" typeId="call_number"/>
<channel id="call_duration" typeId="call_duration"/>
<channel id="call_timestamp" typeId="call_timestamp"/>
<channel id="call_status" typeId="call_status"/>
<channel id="call_name" typeId="call_name"/>
</channels>
</channel-group-type>
<channel-group-type id="accepted">
<label>Accepted Call</label>
<description>The last accepted phone call</description>
<channels>
<channel id="call_number" typeId="caller_number"/>
<channel id="call_duration" typeId="call_duration"/>
<channel id="call_timestamp" typeId="call_timestamp"/>
<channel id="call_name" typeId="caller_name"/>
</channels>
</channel-group-type>
<channel-group-type id="missed">
<label>Missed Call</label>
<description>The last missed phone call</description>
<channels>
<channel id="call_number" typeId="caller_number"/>
<channel id="call_duration" typeId="call_duration"/>
<channel id="call_timestamp" typeId="call_timestamp"/>
<channel id="call_name" typeId="caller_name"/>
</channels>
</channel-group-type>
<channel-group-type id="outgoing">
<label>Outgoing Call</label>
<description>The last outgoing phone call</description>
<channels>
<channel id="call_number" typeId="called_number"/>
<channel id="call_duration" typeId="call_duration"/>
<channel id="call_timestamp" typeId="call_timestamp"/>
<channel id="call_name" typeId="called_name"/>
</channels>
</channel-group-type>
<channel-type id="fwversion" advanced="true">
<item-type>String</item-type>
<label>Firmware Version</label>
<description>Version of the Freebox Server Firmware</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="uptime" advanced="true">
<item-type>Number</item-type>
<label>Server Uptime</label>
<description>Time since last reboot of the Freebox Server</description>
<state readOnly="true" pattern="%d s"/>
</channel-type>
<channel-type id="restarted" advanced="true">
<item-type>Switch</item-type>
<label>Just Restarted</label>
<description>Has the Freebox server have restarted during the last poll period</description>
<category>Alarm</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="reboot">
<item-type>Switch</item-type>
<label>Reboot Freebox</label>
<description>Reboots the Freebox server</description>
<category>Switch</category>
</channel-type>
<channel-type id="tempcpum" advanced="true">
<item-type>Number</item-type>
<label>CPUm Temperature</label>
<description>Actual measured CPU Marvell temperature of the Freebox Server</description>
<category>Temperature</category>
<state readOnly="true" pattern="%d °C"/>
</channel-type>
<channel-type id="tempcpub" advanced="true">
<item-type>Number</item-type>
<label>CPUb Temperature</label>
<description>Actual measured CPU Broadcom (xDSL) temperature of the Freebox Server</description>
<category>Temperature</category>
<state readOnly="true" pattern="%d °C"/>
</channel-type>
<channel-type id="tempswitch" advanced="true">
<item-type>Number</item-type>
<label>Switch Temperature</label>
<description>Actual measured switch temperature of the Freebox Server</description>
<category>Temperature</category>
<state readOnly="true" pattern="%d °C"/>
</channel-type>
<channel-type id="fanspeed" advanced="true">
<item-type>Number</item-type>
<label>Fan Speed</label>
<description>Actual measured fan speed (rpm) of the Freebox Server</description>
<state readOnly="true" pattern="%d rpm"/>
</channel-type>
<channel-type id="lcd_brightness" advanced="true">
<item-type>Number</item-type>
<label>Screen Brightness</label>
<description>Brightness level of the screen in percent</description>
<category>DimmableLight</category>
<state pattern="%d %%"/>
</channel-type>
<channel-type id="lcd_orientation">
<item-type>Number</item-type>
<label>Screen Orientation</label>
<description>Screen Orientation in degrees</description>
<state pattern="%d °">
<options>
<option value="0">Horizontal</option>
<option value="90">Turned left</option>
<option value="180">Reversed</option>
<option value="270">Turned right</option>
</options>
</state>
</channel-type>
<channel-type id="lcd_forced" advanced="true">
<item-type>Switch</item-type>
<label>Forced Orientation</label>
<description>Indicates whether the screen orientation forced</description>
<category>Switch</category>
</channel-type>
<channel-type id="wifi_status">
<item-type>Switch</item-type>
<label>Wifi Enabled</label>
<description>Indicates whether the wifi network is enabled</description>
<category>Switch</category>
</channel-type>
<channel-type id="ftp_status" advanced="true">
<item-type>Switch</item-type>
<label>FTP Server Enabled</label>
<description>Indicates whether the FTP server is enabled</description>
<category>Switch</category>
</channel-type>
<channel-type id="airmedia_status" advanced="true">
<item-type>Switch</item-type>
<label>Air Media Enabled</label>
<description>Indicates whether Air Media is enabled</description>
<category>Switch</category>
</channel-type>
<channel-type id="upnpav_status" advanced="true">
<item-type>Switch</item-type>
<label>UPnP AV Enabled</label>
<description>Indicates whether UPnP AV is enabled</description>
<category>Switch</category>
</channel-type>
<channel-type id="sambafileshare_status" advanced="true">
<item-type>Switch</item-type>
<label>Window File Sharing Enabled</label>
<description>Indicates whether Window File Sharing is enabled</description>
<category>Switch</category>
</channel-type>
<channel-type id="sambaprintershare_status" advanced="true">
<item-type>Switch</item-type>
<label>Window Printer Sharing Enabled</label>
<description>Indicates whether Window Printer Sharing is enabled</description>
<category>Switch</category>
</channel-type>
<channel-type id="xdsl_status">
<item-type>String</item-type>
<label>xDSL Status</label>
<description>Status of the xDSL line</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="ftth_status">
<item-type>Switch</item-type>
<label>FTTH Status</label>
<description>Status of the Fiber Optic line</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="line_status">
<item-type>String</item-type>
<label>Line Status</label>
<description>Status of network line connexion</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="ipv4" advanced="true">
<item-type>String</item-type>
<label>IP Address</label>
<description>Public IP Address of the Freebox Server</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="rate_up" advanced="true">
<item-type>Number</item-type>
<label>Upload Rate</label>
<description>Current upload rate in byte/s</description>
<state readOnly="true" pattern="%d b/s"/>
</channel-type>
<channel-type id="rate_down" advanced="true">
<item-type>Number</item-type>
<label>Download Rate</label>
<description>Current download rate in byte/s</description>
<state readOnly="true" pattern="%d b/s"/>
</channel-type>
<channel-type id="bytes_up" advanced="true">
<item-type>Number</item-type>
<label>Uploaded</label>
<description>Total uploaded bytes since last connection</description>
<state readOnly="true" pattern="%d bytes"/>
</channel-type>
<channel-type id="bytes_down" advanced="true">
<item-type>Number</item-type>
<label>Downloaded</label>
<description>Total downloaded bytes since last connection</description>
<state readOnly="true" pattern="%d bytes"/>
</channel-type>
<channel-type id="onhook">
<item-type>Switch</item-type>
<label>On-hook</label>
<description>Indicates whether the phone is on hook</description>
<category>Switch</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="ringing">
<item-type>Switch</item-type>
<label>Ringing</label>
<description>Is the phone ringing</description>
<category>Alarm</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="call_number">
<item-type>String</item-type>
<label>Phone Number</label>
<description>Called number for outgoing calls. Caller number for incoming calls</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="called_number">
<item-type>String</item-type>
<label>Phone Number</label>
<description>Called number</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="caller_number">
<item-type>String</item-type>
<label>Phone Number</label>
<description>Caller number</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="call_duration">
<item-type>Number</item-type>
<label>Duration</label>
<description>Call duration in seconds</description>
<state readOnly="true" pattern="%d s"/>
</channel-type>
<channel-type id="call_timestamp">
<item-type>DateTime</item-type>
<label>Timestamp</label>
<description>Call creation timestamp</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="call_status">
<item-type>String</item-type>
<label>Call Type</label>
<description>Call Type (ingoing, outgoing, missed)</description>
<state readOnly="true">
<options>
<option value="ingoing">Ingoing call</option>
<option value="outgoing">Outgoing call</option>
<option value="missing">Missed call</option>
</options>
</state>
</channel-type>
<channel-type id="call_name">
<item-type>String</item-type>
<label>Call Name</label>
<description>Called name for outgoing calls. Caller name for incoming calls</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="called_name">
<item-type>String</item-type>
<label>Called Name</label>
<description>Called name</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="caller_name">
<item-type>String</item-type>
<label>Caller Name</label>
<description>Caller name</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="reachable">
<item-type>Switch</item-type>
<label>Reachable</label>
<description>Indicates whether the network device is reachable</description>
<category>Switch</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="playurl">
<item-type>String</item-type>
<label>Play Audio or Video URL</label>
<description>Play an audio or video media from the given URL</description>
</channel-type>
<channel-type id="stop">
<item-type>Switch</item-type>
<label>Stop Playback</label>
<description>Stop the media playback</description>
</channel-type>
</thing:thing-descriptions>

View File

@@ -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-----

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.freebox.internal.api;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.junit.Test;
/**
*
* @author Laurent Garnier - Initial contribution
*/
@NonNullByDefault
public class FreeboxApiManagerTest {
@Test
public void hmacSha1Test() throws Exception {
String expected = "25dad1bb5604321f12b755cc9d755d1480cf7989";
String actual = FreeboxApiManager.hmacSha1("Token1234", "Challenge");
Assert.assertEquals(expected, actual);
}
}