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,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);
}
}