2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.freeboxos.internal.handler;
15 import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
16 import static org.openhab.core.library.unit.Units.*;
18 import java.math.BigDecimal;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.List;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.openhab.binding.freeboxos.internal.action.ServerActions;
27 import org.openhab.binding.freeboxos.internal.api.FreeboxException;
28 import org.openhab.binding.freeboxos.internal.api.rest.AfpManager;
29 import org.openhab.binding.freeboxos.internal.api.rest.AirMediaManager;
30 import org.openhab.binding.freeboxos.internal.api.rest.ConnectionManager;
31 import org.openhab.binding.freeboxos.internal.api.rest.ConnectionManager.FtthStatus;
32 import org.openhab.binding.freeboxos.internal.api.rest.ConnectionManager.Media;
33 import org.openhab.binding.freeboxos.internal.api.rest.ConnectionManager.Status;
34 import org.openhab.binding.freeboxos.internal.api.rest.FtpManager;
35 import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.Source;
36 import org.openhab.binding.freeboxos.internal.api.rest.LanManager;
37 import org.openhab.binding.freeboxos.internal.api.rest.LanManager.LanConfig;
38 import org.openhab.binding.freeboxos.internal.api.rest.SambaManager;
39 import org.openhab.binding.freeboxos.internal.api.rest.SambaManager.Samba;
40 import org.openhab.binding.freeboxos.internal.api.rest.SystemManager;
41 import org.openhab.binding.freeboxos.internal.api.rest.SystemManager.Config;
42 import org.openhab.binding.freeboxos.internal.api.rest.UPnPAVManager;
43 import org.openhab.binding.freeboxos.internal.api.rest.WifiManager;
44 import org.openhab.core.library.types.QuantityType;
45 import org.openhab.core.library.unit.SIUnits;
46 import org.openhab.core.library.unit.Units;
47 import org.openhab.core.thing.Channel;
48 import org.openhab.core.thing.ChannelUID;
49 import org.openhab.core.thing.Thing;
50 import org.openhab.core.thing.binding.ThingHandlerService;
51 import org.openhab.core.thing.binding.builder.ChannelBuilder;
52 import org.openhab.core.thing.type.ChannelTypeUID;
53 import org.openhab.core.types.Command;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
58 * The {@link ServerHandler} handle common parts of Freebox bridges.
60 * @author Gaël L'hopital - Initial contribution
63 public class ServerHandler extends ApiConsumerHandler implements FreeDeviceIntf {
64 private static final BigDecimal HUNDRED = BigDecimal.valueOf(100);
66 private final Logger logger = LoggerFactory.getLogger(ServerHandler.class);
67 private final ChannelUID eventChannelUID;
69 private long uptime = -1;
71 private boolean tryConfigureMediaSink = true;
73 public ServerHandler(Thing thing) {
75 eventChannelUID = new ChannelUID(getThing().getUID(), GROUP_SYS_INFO, BOX_EVENT);
79 void initializeProperties(Map<String, String> properties) throws FreeboxException {
80 LanConfig lanConfig = getManager(LanManager.class).getConfig();
81 Config config = getManager(SystemManager.class).getConfig();
83 properties.put(Thing.PROPERTY_SERIAL_NUMBER, config.serial());
84 properties.put(Thing.PROPERTY_FIRMWARE_VERSION, config.firmwareVersion());
85 properties.put(Thing.PROPERTY_HARDWARE_VERSION, config.modelInfo().prettyName());
86 properties.put(Thing.PROPERTY_MAC_ADDRESS, config.mac().toColonDelimitedString());
87 properties.put(Source.UPNP.name(), lanConfig.name());
89 List<Channel> channels = new ArrayList<>(getThing().getChannels());
91 // Remove channels of the not active media type
92 Status connectionConfig = getManager(ConnectionManager.class).getConfig();
93 channels.removeIf(c -> (GROUP_FTTH.equals(c.getUID().getGroupId()) && connectionConfig.media() != Media.FTTH)
94 || (GROUP_XDSL.equals(c.getUID().getGroupId()) && connectionConfig.media() != Media.XDSL));
96 // Add temperature sensors
97 config.sensors().forEach(sensor -> {
98 ChannelUID sensorId = new ChannelUID(thing.getUID(), GROUP_SENSORS, sensor.id());
99 if (getThing().getChannel(sensorId) == null) {
100 String label = sensor.name();
101 // For revolution, API returns only "Disque dur" so we patch it to have naming consistency with other
102 // temperature sensors
103 if ("Disque dur".equals(label)) {
104 label = "Température " + label;
106 channels.add(ChannelBuilder.create(sensorId).withLabel(label).withAcceptedItemType("Number:Temperature")
107 .withType(new ChannelTypeUID(BINDING_ID, "temperature")).build());
112 config.fans().forEach(sensor -> {
113 ChannelUID sensorId = new ChannelUID(thing.getUID(), GROUP_FANS, sensor.id());
114 if (getThing().getChannel(sensorId) == null) {
115 channels.add(ChannelBuilder.create(sensorId).withLabel(sensor.name())
116 .withAcceptedItemType("Number:Frequency").withType(new ChannelTypeUID(BINDING_ID, "fanspeed"))
121 // And finally update the thing with appropriate channels
122 updateThing(editThing().withChannels(channels).build());
126 protected void internalPoll() throws FreeboxException {
127 logger.debug("Polling server state...");
128 fetchConnectionStatus();
131 if (anyChannelLinked(GROUP_ACTIONS, Set.of(WIFI_STATUS))) {
132 updateChannelOnOff(GROUP_ACTIONS, WIFI_STATUS, getManager(WifiManager.class).getStatus());
134 if (anyChannelLinked(GROUP_ACTIONS, Set.of(AIRMEDIA_STATUS))) {
135 updateChannelOnOff(GROUP_ACTIONS, AIRMEDIA_STATUS, getManager(AirMediaManager.class).getStatus());
137 if (anyChannelLinked(GROUP_ACTIONS, Set.of(UPNPAV_STATUS))) {
138 updateChannelOnOff(GROUP_ACTIONS, UPNPAV_STATUS, getManager(UPnPAVManager.class).getStatus());
141 if (anyChannelLinked(GROUP_FILE_SHARING, Set.of(SAMBA_FILE_STATUS, SAMBA_PRINTER_STATUS))) {
142 Samba response = getManager(SambaManager.class).getConfig();
143 updateChannelOnOff(GROUP_FILE_SHARING, SAMBA_FILE_STATUS, response.fileShareEnabled());
144 updateChannelOnOff(GROUP_FILE_SHARING, SAMBA_PRINTER_STATUS, response.printShareEnabled());
146 if (anyChannelLinked(GROUP_FILE_SHARING, Set.of(FTP_STATUS))) {
147 updateChannelOnOff(GROUP_FILE_SHARING, FTP_STATUS, getManager(FtpManager.class).getStatus());
149 if (anyChannelLinked(GROUP_FILE_SHARING, Set.of(AFP_FILE_STATUS))) {
150 updateChannelOnOff(GROUP_FILE_SHARING, AFP_FILE_STATUS, getManager(AfpManager.class).getStatus());
153 if (tryConfigureMediaSink) {
154 configureMediaSink();
155 tryConfigureMediaSink = false;
160 protected void internalForcePoll() throws FreeboxException {
161 tryConfigureMediaSink = true;
165 private void fetchSystemConfig() throws FreeboxException {
166 Config config = getManager(SystemManager.class).getConfig();
168 config.sensors().forEach(s -> updateChannelQuantity(GROUP_SENSORS, s.id(), s.value(), SIUnits.CELSIUS));
169 config.fans().forEach(f -> updateChannelQuantity(GROUP_FANS, f.id(), f.value(), Units.RPM));
171 uptime = checkUptimeAndFirmware(config.uptimeVal(), uptime, config.firmwareVersion());
172 updateChannelQuantity(GROUP_SYS_INFO, UPTIME, uptime, Units.SECOND);
174 if (anyChannelLinked(GROUP_SYS_INFO, Set.of(IP_ADDRESS))) {
175 LanConfig lanConfig = getManager(LanManager.class).getConfig();
176 updateChannelString(GROUP_SYS_INFO, IP_ADDRESS, lanConfig.ip());
180 private void fetchConnectionStatus() throws FreeboxException {
181 if (anyChannelLinked(GROUP_CONNECTION_STATUS,
182 Set.of(LINE_STATUS, LINE_TYPE, LINE_MEDIA, IP_ADDRESS, IPV6_ADDRESS, BYTES_UP, BYTES_DOWN, RATE + "-up",
183 BW + "-up", PCT_BW + "-up", RATE + "-down", BW + "-down", PCT_BW + "-down"))) {
184 Status status = getManager(ConnectionManager.class).getConfig();
185 updateChannelString(GROUP_CONNECTION_STATUS, LINE_STATUS, status.state());
186 updateChannelString(GROUP_CONNECTION_STATUS, LINE_TYPE, status.type());
187 updateChannelString(GROUP_CONNECTION_STATUS, LINE_MEDIA, status.media());
188 updateChannelString(GROUP_CONNECTION_STATUS, IP_ADDRESS, status.ipv4());
189 updateChannelString(GROUP_CONNECTION_STATUS, IPV6_ADDRESS, status.ipv6());
190 updateRateBandwidth(status.rateUp(), status.bandwidthUp(), "up");
191 updateRateBandwidth(status.rateDown(), status.bandwidthDown(), "down");
193 updateChannelQuantity(GROUP_CONNECTION_STATUS, BYTES_UP, new QuantityType<>(status.bytesUp(), OCTET),
195 updateChannelQuantity(GROUP_CONNECTION_STATUS, BYTES_DOWN, new QuantityType<>(status.bytesDown(), OCTET),
198 if (anyChannelLinked(GROUP_FTTH,
199 Set.of(SFP_PRESENT, SFP_ALIM, SFP_POWER, SFP_SIGNAL, SFP_LINK, SFP_PWR_TX, SFP_PWR_RX))) {
200 FtthStatus ftthStatus = getManager(ConnectionManager.class).getFtthStatus();
201 updateChannelOnOff(GROUP_FTTH, SFP_PRESENT, ftthStatus.sfpPresent());
202 updateChannelOnOff(GROUP_FTTH, SFP_ALIM, ftthStatus.sfpAlimOk());
203 updateChannelOnOff(GROUP_FTTH, SFP_POWER, ftthStatus.sfpHasPowerReport());
204 updateChannelOnOff(GROUP_FTTH, SFP_SIGNAL, ftthStatus.sfpHasSignal());
205 updateChannelOnOff(GROUP_FTTH, SFP_LINK, ftthStatus.link());
206 updateChannelQuantity(GROUP_FTTH, SFP_PWR_TX, ftthStatus.getTransmitDBM(), Units.DECIBEL_MILLIWATTS);
207 updateChannelQuantity(GROUP_FTTH, SFP_PWR_RX, ftthStatus.getReceivedDBM(), Units.DECIBEL_MILLIWATTS);
211 private void updateRateBandwidth(long rate, long bandwidth, String orientation) {
212 QuantityType<?> rateUp = new QuantityType<>(rate * 8, Units.BIT_PER_SECOND);
213 QuantityType<?> bandwidthUp = new QuantityType<>(bandwidth, BIT_PER_SECOND);
214 updateChannelQuantity(GROUP_CONNECTION_STATUS, RATE + "-" + orientation, rateUp, KILOBIT_PER_SECOND);
215 updateChannelQuantity(GROUP_CONNECTION_STATUS, BW + "-" + orientation, bandwidthUp, KILOBIT_PER_SECOND);
216 updateChannelQuantity(GROUP_CONNECTION_STATUS, PCT_BW + "-" + orientation,
217 !bandwidthUp.equals(QuantityType.ZERO) ? rateUp.multiply(HUNDRED).divide(bandwidthUp)
223 protected boolean internalHandleCommand(String channelId, Command command) throws FreeboxException {
224 if (ON_OFF_CLASSES.contains(command.getClass())) {
225 boolean enable = TRUE_COMMANDS.contains(command);
228 updateChannelOnOff(GROUP_ACTIONS, WIFI_STATUS, getManager(WifiManager.class).setStatus(enable));
231 updateChannelOnOff(GROUP_FILE_SHARING, FTP_STATUS, getManager(FtpManager.class).setStatus(enable));
233 case SAMBA_FILE_STATUS:
234 updateChannelOnOff(GROUP_FILE_SHARING, SAMBA_FILE_STATUS,
235 getManager(SambaManager.class).setFileShare(enable));
237 case SAMBA_PRINTER_STATUS:
238 updateChannelOnOff(GROUP_FILE_SHARING, SAMBA_PRINTER_STATUS,
239 getManager(SambaManager.class).setPrintShare(enable));
242 updateChannelOnOff(GROUP_ACTIONS, UPNPAV_STATUS, getManager(UPnPAVManager.class).setStatus(enable));
244 case AFP_FILE_STATUS:
245 updateChannelOnOff(GROUP_FILE_SHARING, AFP_FILE_STATUS,
246 getManager(AfpManager.class).setStatus(enable));
248 case AIRMEDIA_STATUS:
249 updateChannelOnOff(GROUP_ACTIONS, AIRMEDIA_STATUS,
250 getManager(AirMediaManager.class).setStatus(enable));
251 tryConfigureMediaSink = true;
257 return super.internalHandleCommand(channelId, command);
260 public void reboot() {
261 processReboot(() -> {
263 getManager(SystemManager.class).reboot();
264 } catch (FreeboxException e) {
265 logger.warn("Error rebooting: {}", e.getMessage());
271 public Collection<Class<? extends ThingHandlerService>> getServices() {
272 return Set.of(ServerActions.class);
276 public ChannelUID getEventChannelUID() {
277 return eventChannelUID;
281 public void triggerChannel(ChannelUID channelUID, String event) {
282 super.triggerChannel(channelUID, event);