mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[Freeboxos] New binding alternative to Freebox binding (#12342)
* SAT warnings handling Signed-off-by: clinique <gael@lhopital.org> * Correcting potential NPE Signed-off-by: clinique <gael@lhopital.org> * Correcting a NPE on error Signed-off-by: clinique <gael@lhopital.org> * Active player request falls to incorrect API version Signed-off-by: clinique <gael@lhopital.org> * Reintroducing missing capability to send keys to player. Solving an NPE Signed-off-by: clinique <gael@lhopital.org> * Handling DUTY CYCLE more gracefully Signed-off-by: clinique <gael@lhopital.org> * Enhancing DUTY CYCLE Signed-off-by: clinique <gael@lhopital.org> * Moving to SNAPSHOT 3.4 Signed-off-by: clinique <gael@lhopital.org> * Adress inconsistencies in binding name Signed-off-by: clinique <gael@lhopital.org> * Discover Freebox Delta Home equipments(basic_shutter) * Clean previous test code * Fix "Unexpected command" * Fix thing comm error * README for basic shutter * Fix MR discusions and solve maven check errors and warnings * Fix MR discusions * Fix README.md * Enhancing logging to indentify source of erratic warn Signed-off-by: clinique <gael@lhopital.org> * Deny polling a device data when its API is needed and it is OFFLINE Signed-off-by: clinique <gael@lhopital.org> * Taking #11833 in accound Signed-off-by: clinique <gael@lhopital.org> * Switching to Snapshot 4.0.0 Correcting apiDomain was not used as expected Code cleansing. Signed-off-by: clinique <gael@lhopital.org> * Implementing SHUTTER Home Node Signed-off-by: clinique <gael@lhopital.org> * Saving work before instroduction of ArrayListDeserializer Signed-off-by: clinique <gael@lhopital.org> * Enhanced deserialization to simplify code Signed-off-by: clinique <gael@lhopital.org> * Switching to Java 17 records Signed-off-by: clinique <gael@lhopital.org> * Switching to addons.xml, headers updated Signed-off-by: clinique <gael@lhopital.org> * Correcting two errors. Signed-off-by: clinique <gael@lhopital.org> * Enhance usage of global variables Signed-off-by: clinique <gael@lhopital.org> * Some code enhancement for base classes Signed-off-by: clinique <gael@lhopital.org> * solving SAT issues Signed-off-by: clinique <gael@lhopital.org> * Adding IliadBox compatibility Signed-off-by: clinique <gael@lhopital.org> * Commiting work Signed-off-by: clinique <gael@lhopital.org> * Saving work Signed-off-by: clinique <gael@lhopital.org> * Rebooting Home Node part Signed-off-by: clinique <gael@lhopital.org> * Spotless apply Signed-off-by: clinique <gael@lhopital.org> * Adding i18n Signed-off-by: clinique <gael@lhopital.org> * Decreasing websocket logging level Signed-off-by: clinique <gael@lhopital.org> * SAT warnings handling Signed-off-by: clinique <gael@lhopital.org> * Correcting potential NPE Signed-off-by: clinique <gael@lhopital.org> * Correcting a NPE on error Signed-off-by: clinique <gael@lhopital.org> * Active player request falls to incorrect API version Signed-off-by: clinique <gael@lhopital.org> * Reintroducing missing capability to send keys to player. Solving an NPE Signed-off-by: clinique <gael@lhopital.org> * Handling DUTY CYCLE more gracefully Signed-off-by: clinique <gael@lhopital.org> * Enhancing DUTY CYCLE Signed-off-by: clinique <gael@lhopital.org> * Moving to SNAPSHOT 3.4 Signed-off-by: clinique <gael@lhopital.org> * Adress inconsistencies in binding name Signed-off-by: clinique <gael@lhopital.org> * Discover Freebox Delta Home equipments(basic_shutter) * Clean previous test code * Fix "Unexpected command" * Fix thing comm error * README for basic shutter * Fix MR discusions and solve maven check errors and warnings * Fix MR discusions * Fix README.md * Enhancing logging to indentify source of erratic warn Signed-off-by: clinique <gael@lhopital.org> * Deny polling a device data when its API is needed and it is OFFLINE Signed-off-by: clinique <gael@lhopital.org> * Taking #11833 in accound Signed-off-by: clinique <gael@lhopital.org> * Switching to Snapshot 4.0.0 Correcting apiDomain was not used as expected Code cleansing. Signed-off-by: clinique <gael@lhopital.org> * Implementing SHUTTER Home Node Signed-off-by: clinique <gael@lhopital.org> * Saving work before instroduction of ArrayListDeserializer Signed-off-by: clinique <gael@lhopital.org> * Enhanced deserialization to simplify code Signed-off-by: clinique <gael@lhopital.org> * Switching to Java 17 records Signed-off-by: clinique <gael@lhopital.org> * Switching to addons.xml, headers updated Signed-off-by: clinique <gael@lhopital.org> * Correcting two errors. Signed-off-by: clinique <gael@lhopital.org> * Enhance usage of global variables Signed-off-by: clinique <gael@lhopital.org> * Some code enhancement for base classes Signed-off-by: clinique <gael@lhopital.org> * solving SAT issues Signed-off-by: clinique <gael@lhopital.org> * Adding IliadBox compatibility Signed-off-by: clinique <gael@lhopital.org> * Commiting work Signed-off-by: clinique <gael@lhopital.org> * Saving work Signed-off-by: clinique <gael@lhopital.org> * Rebooting Home Node part Signed-off-by: clinique <gael@lhopital.org> * Spotless apply Signed-off-by: clinique <gael@lhopital.org> * Enhancing SAT report Signed-off-by: clinique <gael@lhopital.org> * I think that mvn spotless:apply has a problem with records - trying once again Signed-off-by: clinique <gael@lhopital.org> * Avoid requesting detailed information for a shutdown repeater. Signed-off-by: clinique <gael@lhopital.org> * Switched fan speed to RPM unit Signed-off-by: clinique <gael@lhopital.org> * Correcting SAT Signed-off-by: clinique <gael@lhopital.org> * Correcting SAT Signed-off-by: clinique <gael@lhopital.org> * Divergence between eclipse and mvn spotless:apply Signed-off-by: clinique <gael@lhopital.org> * YASAT Signed-off-by: clinique <gael@lhopital.org> * Corrections following fwolter code review Signed-off-by: clinique <gael@lhopital.org> * Pleasing SAT Signed-off-by: clinique <gael@lhopital.org> * Second fwolter code review Signed-off-by: clinique <gael@lhopital.org> * Porting modifications introduced in PR #15121 Signed-off-by: clinique <gael@lhopital.org> * Removing redundant null checks. Signed-off-by: clinique <gael@lhopital.org> * Rebased. Signed-off-by: clinique <gael@lhopital.org> * Trying to remove the last sleep. Signed-off-by: clinique <gael@lhopital.org> * Reporting modifications of PR #15121 Signed-off-by: clinique <gael@lhopital.org> * Reverting to working and cleaner granting process Signed-off-by: clinique <gael@lhopital.org> * Removing last Thread:Sleep Signed-off-by: clinique <gael@lhopital.org> * spotless:apply Signed-off-by: clinique <gael@lhopital.org> --------- Signed-off-by: clinique <gael@lhopital.org> Co-authored-by: ben.12 <benmor_12@yahoo.fr>
This commit is contained in:
parent
123982dcc6
commit
751c8a74c4
@ -111,6 +111,7 @@
|
||||
/bundles/org.openhab.binding.folding/ @fa2k
|
||||
/bundles/org.openhab.binding.foobot/ @airboxlab @Hilbrand
|
||||
/bundles/org.openhab.binding.freebox/ @lolodomo
|
||||
/bundles/org.openhab.binding.freeboxos/ @clinique
|
||||
/bundles/org.openhab.binding.fronius/ @trokohl
|
||||
/bundles/org.openhab.binding.fsinternetradio/ @paphko
|
||||
/bundles/org.openhab.binding.ftpupload/ @paulianttila
|
||||
|
@ -551,6 +551,11 @@
|
||||
<artifactId>org.openhab.binding.freebox</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.freeboxos</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.fronius</artifactId>
|
||||
|
20
bundles/org.openhab.binding.freeboxos/NOTICE
Normal file
20
bundles/org.openhab.binding.freeboxos/NOTICE
Normal file
@ -0,0 +1,20 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
|
||||
== Third-party Content
|
||||
|
||||
IPAddress: Java library for handling IP addresses and subnets, both IPv4 and IPv6
|
||||
* License: Apache License 2.0
|
||||
* Project: https://github.com/seancfoley/IPAddress
|
||||
* Source: https://github.com/seancfoley/IPAddress
|
210
bundles/org.openhab.binding.freeboxos/README.md
Normal file
210
bundles/org.openhab.binding.freeboxos/README.md
Normal file
@ -0,0 +1,210 @@
|
||||
# FreeboxOS Binding
|
||||
|
||||
Free is a French telecom operator providing advanced equipments to manage your broadband access.
|
||||
|
||||
This binding integrates the [Freebox Revolution](https://www.free.fr/freebox/freebox-revolution/) and [Freebox Delta](https://www.free.fr/freebox/freebox-delta/) to your openHAB installation.
|
||||
|
||||
The server can be connected to Free infrastructures either by xDSL or fiber optic.
|
||||
|
||||
This binding provides metrics about your network bridge/router and attached devices (wifi repeaters, TV boxes ...).
|
||||
It also provides home automation capabilities when appropriate dongle has been inserted in the server.
|
||||
|
||||
IliadBox, italian version of the Freebox Pop are also compatible.
|
||||
|
||||
## Supported Things
|
||||
|
||||
This binding supports the following thing types:
|
||||
|
||||
| Thing | Thing Type | Description |
|
||||
|-------------------|------------|---------------------------------------------------------------|
|
||||
| api | Bridge | Bridge to access freebox OS API hosted by the server |
|
||||
| delta | Thing | A Freebox Delta server |
|
||||
| revolution | Thing | A Freebox Revolution server |
|
||||
| player | Thing | A TV player equipment |
|
||||
| active-player | Thing | The TV player equipment with API capabilities (e.g. Devialet) |
|
||||
| landline | Thing | The phone wired to the Freebox Server |
|
||||
| host | Thing | A network device on the local network |
|
||||
| wifihost | Thing | A wifi networked device on the local network |
|
||||
| vm (*) | Thing | A virtual machine hosted on the server |
|
||||
| freeplug | Thing | A virtual machine hosted on the server |
|
||||
| repeater | Thing | A Free wifi repeater |
|
||||
| basic-shutter (*) | Thing | RTS Shutter configured in Freebox Home |
|
||||
| shutter (*) | Thing | IO Home Control shutter configured in Freebox Home |
|
||||
| kfb (*) | Thing | A keyfob associated with your alarm system |
|
||||
| alarm (*) | Thing | The Freebox Home Alarm System |
|
||||
|
||||
(*) Restricted to servers >= Delta
|
||||
|
||||
## Discovery
|
||||
|
||||
The API bridge is discovered automatically through mDNS in the local network.
|
||||
After the bridge is discovered and available to openHAB, the binding will automatically discover phone, network devices available on the local network.
|
||||
Note that the discovered thing will be setup to use only HTTP API (and not HTTPS) because only an IP can be automatically determined while a domain name is required to use HTTPS with a valid certificate.
|
||||
|
||||
## Binding configuration
|
||||
|
||||
FreeboxOS binding has the following configuration parameters:
|
||||
|
||||
| Name | Description | Mandatory |
|
||||
|-----------------|----------------------------------------------------|-----------|
|
||||
| timeout | The timeout for reading from the device in seconds | yes |
|
||||
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
### API bridge
|
||||
|
||||
| Parameter Label | Parameter ID | Description | Required | Default |
|
||||
|--------------------------|-------------------|--------------------------------------------------------|----------|----------------------|
|
||||
| Freebox Server Address | apiDomain | The domain to use in place of hardcoded Freebox ip | No | mafreebox.freebox.fr |
|
||||
| Application Token | appToken | Token generated by the Freebox Server. | Yes | |
|
||||
| Network Device Discovery | discoverNetDevice | Enable the discovery of network device things. | No | false |
|
||||
| HTTPS Available | httpsAvailable | Tells if https has been configured on the Freebox | No | false |
|
||||
| HTTPS port | httpsPort | Port to use for remote https access to the Freebox Api | No | 15682 |
|
||||
|
||||
If the parameter *apiDomain* is not set, the binding will use the default address used by Free to access your Freebox Server (mafreebox.freebox.fr).
|
||||
The bridge thing will initialize only if a valid application token (parameter *appToken*) is filled.
|
||||
|
||||
### Server: Revolution or Delta
|
||||
|
||||
The *revolution* or *delta* thing requires the following configuration parameters:
|
||||
|
||||
| Parameter Label | Parameter ID | Description | Required | Default |
|
||||
|------------------|-----------------|--------------------------------------------------------------------------|----------|---------|
|
||||
| Refresh Interval | refreshInterval | The refresh interval (seconds) which is used to poll the Freebox Server. | No | 30 |
|
||||
|
||||
### Player thing
|
||||
|
||||
The *player* thing requires the following configuration parameters:
|
||||
|
||||
| Parameter Label | Parameter ID | Description | Required | Default |
|
||||
|------------------|-----------------|----------------------------------------------------------------------------|----------|---------|
|
||||
| MAC Address | macAddress | The MAC address of the player device. | Yes | |
|
||||
| ID | id | Id of the player within Freebox Api | Yes | 1 |
|
||||
| Player port | port | | No | 24322 |
|
||||
| Password | password | AirPlay password | No | |
|
||||
| Remote Code | remoteCode | Code associated to remote control | No | |
|
||||
| Accept all MP3 | acceptAllMp3 | Accept any bitrate for MP3 audio or only bitrates greater than 64 kbps | No | true |
|
||||
| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll the player | Yes | 30 |
|
||||
| Callback URL | callbackUrl | URL to use for playing notification sounds, e.g. 'http://192.168.0.2:8080' | No | |
|
||||
|
||||
### Landline
|
||||
|
||||
The *landline* thing requires the following configuration parameters:
|
||||
|
||||
| Parameter Label | Parameter ID | Description | Required | Default |
|
||||
|------------------|-----------------|------------------------------------------------------------------------|----------|---------|
|
||||
| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll for phone state. | No | 2 |
|
||||
|
||||
### Network devices: Host and WifiHost
|
||||
|
||||
The *host* and *wifihost* things requires the following configuration parameters:
|
||||
|
||||
| Parameter Label | Parameter ID | Description | Required | Default |
|
||||
|------------------|-----------------|------------------------------------------------------------------------|----------|---------|
|
||||
| MAC Address | macAddress | The MAC address of the network host . | Yes | |
|
||||
| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll for phone state. | No | 30 |
|
||||
|
||||
### Repeater and Vm thing
|
||||
|
||||
The *repeater* thing is a specialized case of a *wifihost*. The *vm* derives from *host*. They share the same configuration definition:
|
||||
|
||||
| Parameter Label | Parameter ID | Description | Required | Default |
|
||||
|------------------|-----------------|------------------------------------------------------------------|----------|---------|
|
||||
| MAC Address | macAddress | The MAC address of the player device. | No | |
|
||||
| ID | id | Id of the repeater within Freebox Api | Yes | 1 |
|
||||
| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll the player | Yes | 30 |
|
||||
|
||||
### Basic shutter thing
|
||||
|
||||
The *basic-shutter* thing requires the following configuration parameters:
|
||||
|
||||
| Parameter Label | Parameter ID | Description | Required | Default |
|
||||
|------------------|-----------------|------------------------------------------------------------------|----------|---------|
|
||||
| ID | id | Id of the Home Node | Yes | 1 |
|
||||
| UP Slot ID | upSlotId | Id of the UP Slot. | Yes | 0 |
|
||||
| STOP Slot ID | stopSlotId | Id of the STOP Slot. | Yes | 1 |
|
||||
| DOWN Slot ID | downSlotId | Id of the DOWN Slot. | Yes | 2 |
|
||||
| STATE Signal ID | stateSignalId | Id of the STATE Signal. | Yes | 3 |
|
||||
|
||||
## Authentication
|
||||
|
||||
You will have to authorize openHAB to connect to your Freebox. Here is the process described:
|
||||
|
||||
**Step 1** At binding startup, if no token is recorded in the Freebox Server (bridge) configuration, the binding will run a pairing request
|
||||
|
||||
**Step 2** Run to your Freebox and approve the pairing request for openHAB FreeboxOS Binding that is displayed on the Freebox screen
|
||||
|
||||
**Step 3** the application token is automatically recorded in the Freebox Server (bridge) configuration
|
||||
|
||||
**Step 4** you can use the console command `freeboxos apptoken` to display the application token and use it later to set up your thing in a configuration file
|
||||
|
||||
**Step 5** log in your Freebox admin console to allocate needed rights to FreeboxOS Binding
|
||||
|
||||
Once initialized, the thing will generate all available channels.
|
||||
|
||||
## Channels
|
||||
|
||||
The following channels are supported:
|
||||
|
||||
| Thing | Channel Type ID | Item Type | Access Mode | Description |
|
||||
|---------------|-----------------------------|---------------|-------------|--------------------------------------------------------------------------------|
|
||||
| revolution | lcd-brightness | Number | RW | Brightness level of the screen in percent |
|
||||
| revolution | lcd-orientation | Number | RW | Screen Orientation in degrees (0 or 90 or 180 or 270) |
|
||||
| revolution | lcd-forced | Switch | RW | Indicates whether the screen orientation forced |
|
||||
| server (*) | uptime | Number | R | Time since last reboot of the Freebox Server |
|
||||
| server (*) | restarted | Switch | R | Indicates whether the Freebox server has restarted during the last poll period |
|
||||
| server (*) | wifi-status | Switch | RW | Indicates whether the WiFi network is enabled |
|
||||
| server (*) | ftp-status | Switch | RW | Indicates whether the FTP server is enabled |
|
||||
| server (*) | airmedia-status | Switch | RW | Indicates whether Air Media is enabled |
|
||||
| server (*) | upnpav-status | Switch | RW | Indicates whether UPnP AV is enabled |
|
||||
| server (*) | samba-file-status | Switch | RW | Indicates whether Window File Sharing is enabled |
|
||||
| server (*) | samba-printer-status | Switch | RW | Indicates whether Window Printer Sharing is enabled |
|
||||
| server (*) | xdsl-status | String | R | Status of the xDSL line |
|
||||
| server (*) | ftth-status | Switch | R | Status of the Ftth line |
|
||||
| server (*) | line-status | String | R | Status of network line connexion |
|
||||
| server (*) | ipv4 | String | R | Public IP Address of the Freebox Server |
|
||||
| server (*) | rate-up | Number | R | Current upload rate in byte/s |
|
||||
| server (*) | rate-down | Number | R | Current download rate in byte/s |
|
||||
| server (*) | bytes-up | Number | R | Total uploaded bytes since last connection |
|
||||
| server (*) | bytes-down | Number | R | Total downloaded bytes since last connection |
|
||||
| phone | state#onhook | Switch | R | Indicates whether the phone is on hook |
|
||||
| phone | state#ringing | Switch | R | Is the phone ringing |
|
||||
| call | incoming#number | Call | R | Current incoming call number |
|
||||
| call | incoming#timestamp | DateTime | R | Current incoming call creation timestamp |
|
||||
| call | incoming#name | String | R | Current incoming caller name |
|
||||
| call | accepted#number | Call | R | Last accepted call number |
|
||||
| call | accepted#duration | Number | R | Last accepted call duration in seconds |
|
||||
| call | accepted#timestamp | DateTime | R | Last accepted call creation timestamp |
|
||||
| call | accepted#name | String | R | Last accepted caller name |
|
||||
| call | missed#number | Call | R | Last missed call number |
|
||||
| call | missed#timestamp | DateTime | R | Last missed call creation timestamp |
|
||||
| call | missed#name | String | R | Last missed caller name |
|
||||
| call | outgoing#number | Call | R | Last outgoing call number |
|
||||
| call | outgoing#duration | Number | R | Last outgoing call duration in seconds |
|
||||
| call | outgoing#timestamp | DateTime | R | Last outgoing call creation timestamp |
|
||||
| call | outgoing#name | String | R | Last outgoing called name |
|
||||
| net_device | reachable | Switch | R | Indicates whether the network device is reachable |
|
||||
| net_interface | reachable | Switch | R | Indicates whether the network interface is reachable |
|
||||
| airplay | playurl | String | W | Play an audio or video media from the given URL |
|
||||
| airplay | stop | Switch | W | Stop the media playback |
|
||||
| basic-shutter | basic-shutter#basic-shutter | RollerShutter | W | Up, stop and down commands for a RTS shutter |
|
||||
|
||||
(*): server means *delta* or *revolution*
|
||||
|
||||
## Actions for rules
|
||||
|
||||
The following actions are available in rules/scripting:
|
||||
|
||||
| Thing Type | Action Name | Parameters | Description |
|
||||
|-------------|------------------|-------------------------|------------------------------------------------------|
|
||||
| host | wol | None | Sends a wake on lan packet to the lan connected host |
|
||||
| player | reboot | None | Reboots the player device |
|
||||
| player | sendKey | key: String | Send a key (remote emulation) to the player |
|
||||
| player | sendLongKey | key: String | Sends the key emulating a longpress on the button |
|
||||
| player | sendMultipleKeys | keys: String | Sends multiple keys to the player, comma separated |
|
||||
| player | sendKeyRepeat | key: String, count: int | Sends the key multiple times |
|
||||
| server | reboot | None | Reboots the Freebox Server |
|
||||
| freeplug | reset | None | Resets the Freeplug |
|
||||
| call | reset | None | Clears the call log queue |
|
||||
| repeater | reboot | None | Reboots the Repeater |
|
30
bundles/org.openhab.binding.freeboxos/pom.xml
Normal file
30
bundles/org.openhab.binding.freeboxos/pom.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.freeboxos</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: FreeboxOS Binding</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.seancfoley</groupId>
|
||||
<artifactId>ipaddress</artifactId>
|
||||
<version>5.4.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.freeboxos-${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-freeboxos" description="Freebox OS 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.freeboxos/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.Category;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.UpDownType;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link FreeboxBinding} class defines common constants, which are used across the binding.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FreeboxOsBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "freeboxos";
|
||||
|
||||
// List of all Bridge Type UIDs
|
||||
public static final ThingTypeUID BRIDGE_TYPE_API = new ThingTypeUID(BINDING_ID, "api");
|
||||
|
||||
// Thing Types ID strings
|
||||
private static final String THING_DECT = "dect";
|
||||
private static final String THING_FXS = "fxs";
|
||||
private static final String THING_REVOLUTION = "revolution";
|
||||
private static final String THING_DELTA = "delta";
|
||||
private static final String THING_WIFI_HOST = "wifihost";
|
||||
private static final String THING_ACTIVE_PLAYER = "active-player";
|
||||
|
||||
public static final String THING_FREEPLUG = "freeplug";
|
||||
public static final String THING_VM = "vm";
|
||||
public static final String THING_CALL = "call";
|
||||
public static final String THING_HOST = "host";
|
||||
public static final String THING_PLAYER = "player";
|
||||
public static final String THING_REPEATER = "repeater";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_REVOLUTION = new ThingTypeUID(BINDING_ID, THING_REVOLUTION);
|
||||
public static final ThingTypeUID THING_TYPE_DELTA = new ThingTypeUID(BINDING_ID, THING_DELTA);
|
||||
public static final ThingTypeUID THING_TYPE_FXS = new ThingTypeUID(BINDING_ID, THING_FXS);
|
||||
public static final ThingTypeUID THING_TYPE_DECT = new ThingTypeUID(BINDING_ID, THING_DECT);
|
||||
public static final ThingTypeUID THING_TYPE_CALL = new ThingTypeUID(BINDING_ID, THING_CALL);
|
||||
public static final ThingTypeUID THING_TYPE_FREEPLUG = new ThingTypeUID(BINDING_ID, THING_FREEPLUG);
|
||||
public static final ThingTypeUID THING_TYPE_HOST = new ThingTypeUID(BINDING_ID, THING_HOST);
|
||||
public static final ThingTypeUID THING_TYPE_WIFI_HOST = new ThingTypeUID(BINDING_ID, THING_WIFI_HOST);
|
||||
public static final ThingTypeUID THING_TYPE_PLAYER = new ThingTypeUID(BINDING_ID, THING_PLAYER);
|
||||
public static final ThingTypeUID THING_TYPE_ACTIVE_PLAYER = new ThingTypeUID(BINDING_ID, THING_ACTIVE_PLAYER);
|
||||
public static final ThingTypeUID THING_TYPE_VM = new ThingTypeUID(BINDING_ID, THING_VM);
|
||||
public static final ThingTypeUID THING_TYPE_REPEATER = new ThingTypeUID(BINDING_ID, THING_REPEATER);
|
||||
|
||||
// All supported Thing types
|
||||
public static final Set<ThingTypeUID> BRIDGE_TYPE_UIDS = Set.of(BRIDGE_TYPE_API);
|
||||
public static final Set<ThingTypeUID> THINGS_TYPES_UIDS = Set.of(THING_TYPE_FXS, THING_TYPE_DECT, THING_TYPE_CALL,
|
||||
THING_TYPE_HOST, THING_TYPE_VM, THING_TYPE_PLAYER, THING_TYPE_ACTIVE_PLAYER, THING_TYPE_DELTA,
|
||||
THING_TYPE_REVOLUTION, THING_TYPE_REPEATER, THING_TYPE_WIFI_HOST, THING_TYPE_FREEPLUG);
|
||||
public static final Set<ThingTypeUID> HOME_TYPES_UIDS = Set.of(Category.BASIC_SHUTTER.getThingTypeUID(),
|
||||
Category.SHUTTER.getThingTypeUID(), Category.KFB.getThingTypeUID(), Category.CAMERA.getThingTypeUID(),
|
||||
Category.ALARM.getThingTypeUID());
|
||||
|
||||
protected static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
|
||||
.of(BRIDGE_TYPE_UIDS, THINGS_TYPES_UIDS, HOME_TYPES_UIDS).flatMap(Set::stream).collect(Collectors.toSet());
|
||||
|
||||
// Thing properties
|
||||
// public static final String LAST_CALL_TIMESTAMP = "lastCallTimestamp";
|
||||
public static final String ROLE = "role";
|
||||
public static final String NET_ID = "netId";
|
||||
public static final String ETHERNET_SPEED = "ethernetSpeed";
|
||||
public static final String LOCAL = "local";
|
||||
public static final String FULL_DUPLEX = "fullDuplex";
|
||||
|
||||
// List of all Group Channel ids
|
||||
public static final String GROUP_SENSORS = "sensors";
|
||||
public static final String GROUP_FANS = "fans";
|
||||
public static final String CONNECTION_STATUS = "connection-status";
|
||||
public static final String SYS_INFO = "sysinfo";
|
||||
public static final String ACTIONS = "actions";
|
||||
public static final String FILE_SHARING = "file-sharing";
|
||||
public static final String CONNECTIVITY = "connectivity";
|
||||
public static final String DISPLAY = "display";
|
||||
public static final String VM_STATUS = "vmstatus";
|
||||
public static final String GROUP_WIFI = "wifi";
|
||||
public static final String REPEATER_MISC = "repeater-misc";
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String RSSI = "rssi";
|
||||
public static final String SSID = "ssid";
|
||||
public static final String WIFI_QUALITY = "wifi-quality";
|
||||
public static final String WIFI_HOST = "wifi-host";
|
||||
public static final String UPTIME = "uptime";
|
||||
public static final String BOX_EVENT = "box-event";
|
||||
public static final String LCD_BRIGHTNESS = "lcd-brightness";
|
||||
public static final String LCD_ORIENTATION = "lcd-orientation";
|
||||
public static final String LCD_FORCED = "lcd-forced";
|
||||
public static final String WIFI_STATUS = "wifi-status";
|
||||
public static final String IP_ADDRESS = "ip-address";
|
||||
public static final String IPV6_ADDRESS = "ipv6-address";
|
||||
public static final String LINE_STATUS = "line-status";
|
||||
public static final String LINE_TYPE = "line-type";
|
||||
public static final String LINE_MEDIA = "line-media";
|
||||
public static final String PLAYER_STATUS = "player-status";
|
||||
public static final String PACKAGE = "package";
|
||||
public static final String RATE = "rate";
|
||||
public static final String BYTES_UP = "bytes-up";
|
||||
public static final String BYTES_DOWN = "bytes-down";
|
||||
public static final String BW = "bandwidth";
|
||||
public static final String PCT_BW = "bandwidth-usage";
|
||||
public static final String ONHOOK = "onhook";
|
||||
public static final String RINGING = "ringing";
|
||||
public static final String HARDWARE_STATUS = "hardware-status";
|
||||
public static final String TELEPHONY_SERVICE = "telephony-service";
|
||||
public static final String GAIN_RX = "gain-rx";
|
||||
public static final String GAIN_TX = "gain-tx";
|
||||
public static final String FTP_STATUS = "ftp-status";
|
||||
public static final String SAMBA_FILE_STATUS = "samba-file-status";
|
||||
public static final String SAMBA_PRINTER_STATUS = "samba-printer-status";
|
||||
public static final String AFP_FILE_STATUS = "afp-file-status";
|
||||
public static final String REACHABLE = "reachable";
|
||||
public static final String LAST_SEEN = "last-seen";
|
||||
public static final String ALTERNATE_RING = "lcd-forced";
|
||||
public static final String DECT_ACTIVE = "dect-active";
|
||||
public static final String UPNPAV_STATUS = "upnpav-status";
|
||||
|
||||
// Call channels for groups Accepted, Missed and Outgoing
|
||||
public static final String NUMBER = "number";
|
||||
public static final String DURATION = "duration";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
public static final String NAME = "name";
|
||||
|
||||
// Freebox player channels
|
||||
public static final String AIRMEDIA_STATUS = "airmedia-status";
|
||||
public static final String KEY_CODE = "key-code";
|
||||
|
||||
// Virtual machine channels
|
||||
public static final String STATUS = "status";
|
||||
|
||||
// Repeater channels
|
||||
public static final String LED = "led";
|
||||
public static final String HOST_COUNT = "host-count";
|
||||
|
||||
// Home channels
|
||||
public static final String KEYFOB_ENABLE = "enable";
|
||||
public static final String NODE_BATTERY = "battery";
|
||||
public static final String SHUTTER_POSITION = "position-set";
|
||||
public static final String SHUTTER_STOP = "stop";
|
||||
public static final String BASIC_SHUTTER_STATE = "state";
|
||||
public static final String BASIC_SHUTTER_UP = "up";
|
||||
public static final String BASIC_SHUTTER_DOWN = "down";
|
||||
// public static final String BASIC_SHUTTER_CMD = "basic-shutter";
|
||||
public static final String ALARM_PIN = "pin";
|
||||
public static final String ALARM_SOUND = "sound";
|
||||
public static final String ALARM_VOLUME = "volume";
|
||||
public static final String ALARM_TIMEOUT1 = "timeout1";
|
||||
public static final String ALARM_TIMEOUT2 = "timeout2";
|
||||
public static final String ALARM_TIMEOUT3 = "timeout3";
|
||||
|
||||
public static final Set<Command> TRUE_COMMANDS = Set.of(OnOffType.ON, UpDownType.UP, OpenClosedType.OPEN);
|
||||
public static final Set<Class<?>> ON_OFF_CLASSES = Set.of(OnOffType.class, UpDownType.class, OpenClosedType.class);
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.freeboxos.internal.api.ApiHandler;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.Category;
|
||||
import org.openhab.binding.freeboxos.internal.handler.ActivePlayerHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.AlarmHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.BasicShutterHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.CallHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.CameraHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.DectHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.FreeboxOsHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.FreeplugHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.FxsHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.HostHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.KeyfobHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.PlayerHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.RepeaterHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.RevolutionHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.ServerHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.ShutterHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.VmHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.WifiStationHandler;
|
||||
import org.openhab.core.audio.AudioHTTPServer;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
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.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.ComponentContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link FreeboxOsHandlerFactory} is responsible for creating things and thing handlers.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.freeboxos")
|
||||
public class FreeboxOsHandlerFactory extends BaseThingHandlerFactory {
|
||||
private static final String TIMEOUT = "timeout";
|
||||
private static final String CALLBACK_URL = "callBackUrl";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FreeboxOsHandlerFactory.class);
|
||||
|
||||
private final NetworkAddressService networkAddressService;
|
||||
private final AudioHTTPServer audioHTTPServer;
|
||||
private final HttpClient httpClient;
|
||||
private final ApiHandler apiHandler;
|
||||
private String callbackURL = "";
|
||||
|
||||
@Activate
|
||||
public FreeboxOsHandlerFactory(final @Reference AudioHTTPServer audioHTTPServer,
|
||||
final @Reference NetworkAddressService networkAddressService,
|
||||
final @Reference HttpClientFactory httpClientFactory, final @Reference TimeZoneProvider timeZoneProvider,
|
||||
ComponentContext componentContext, Map<String, Object> config) {
|
||||
super.activate(componentContext);
|
||||
|
||||
this.audioHTTPServer = audioHTTPServer;
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.networkAddressService = networkAddressService;
|
||||
this.apiHandler = new ApiHandler(httpClient, timeZoneProvider);
|
||||
|
||||
configChanged(config);
|
||||
}
|
||||
|
||||
@Modified
|
||||
public void configChanged(Map<String, Object> config) {
|
||||
String timeout = (String) config.getOrDefault(TIMEOUT, "8");
|
||||
apiHandler.setTimeout(TimeUnit.SECONDS.toMillis(Long.parseLong(timeout)));
|
||||
|
||||
callbackURL = (String) config.getOrDefault(CALLBACK_URL, "");
|
||||
int port = HttpServiceUtil.getHttpServicePort(bundleContext);
|
||||
if (callbackURL.isEmpty() && port != -1) {
|
||||
String openHabIp = Objects.requireNonNull(networkAddressService.getPrimaryIpv4HostAddress());
|
||||
// we do not use SSL as it can cause certificate validation issues.
|
||||
callbackURL = "http://%s:%d".formatted(openHabIp, port);
|
||||
}
|
||||
if (callbackURL.isEmpty()) {
|
||||
logger.warn("Unable to build a correct call back URL to stream media contents");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (BRIDGE_TYPE_API.equals(thingTypeUID)) {
|
||||
return new FreeboxOsHandler((Bridge) thing, new FreeboxOsSession(apiHandler), callbackURL, bundleContext,
|
||||
audioHTTPServer);
|
||||
} else if (THING_TYPE_FREEPLUG.equals(thingTypeUID)) {
|
||||
return new FreeplugHandler(thing);
|
||||
} else if (THING_TYPE_FXS.equals(thingTypeUID)) {
|
||||
return new FxsHandler(thing);
|
||||
} else if (THING_TYPE_DECT.equals(thingTypeUID)) {
|
||||
return new DectHandler(thing);
|
||||
} else if (THING_TYPE_CALL.equals(thingTypeUID)) {
|
||||
return new CallHandler(thing);
|
||||
} else if (THING_TYPE_REVOLUTION.equals(thingTypeUID)) {
|
||||
return new RevolutionHandler(thing);
|
||||
} else if (THING_TYPE_DELTA.equals(thingTypeUID)) {
|
||||
return new ServerHandler(thing);
|
||||
} else if (THING_TYPE_HOST.equals(thingTypeUID)) {
|
||||
return new HostHandler(thing);
|
||||
} else if (THING_TYPE_WIFI_HOST.equals(thingTypeUID)) {
|
||||
return new WifiStationHandler(thing);
|
||||
} else if (THING_TYPE_REPEATER.equals(thingTypeUID)) {
|
||||
return new RepeaterHandler(thing);
|
||||
} else if (THING_TYPE_VM.equals(thingTypeUID)) {
|
||||
return new VmHandler(thing);
|
||||
} else if (THING_TYPE_ACTIVE_PLAYER.equals(thingTypeUID)) {
|
||||
return new ActivePlayerHandler(thing);
|
||||
} else if (THING_TYPE_PLAYER.equals(thingTypeUID)) {
|
||||
return new PlayerHandler(thing);
|
||||
} else if (Category.BASIC_SHUTTER.getThingTypeUID().equals(thingTypeUID)) {
|
||||
return new BasicShutterHandler(thing);
|
||||
} else if (Category.SHUTTER.getThingTypeUID().equals(thingTypeUID)) {
|
||||
return new ShutterHandler(thing);
|
||||
} else if (Category.ALARM.getThingTypeUID().equals(thingTypeUID)) {
|
||||
return new AlarmHandler(thing);
|
||||
} else if (Category.KFB.getThingTypeUID().equals(thingTypeUID)) {
|
||||
return new KeyfobHandler(thing);
|
||||
} else if (Category.CAMERA.getThingTypeUID().equals(thingTypeUID)) {
|
||||
return new CameraHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.action;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.handler.ActivePlayerHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.PlayerHandler;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {ActivePlayerActions} class is responsible to call corresponding actions on Freebox Player with API
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@ThingActionsScope(name = "freeboxos")
|
||||
@NonNullByDefault
|
||||
public class ActivePlayerActions extends PlayerActions {
|
||||
private final Logger logger = LoggerFactory.getLogger(ActivePlayerActions.class);
|
||||
|
||||
@RuleAction(label = "reboot freebox player", description = "Reboots the Freebox Player")
|
||||
public void reboot() {
|
||||
logger.debug("Player reboot called");
|
||||
PlayerHandler localHandler = this.handler;
|
||||
if (localHandler instanceof ActivePlayerHandler apHandler) {
|
||||
apHandler.reboot();
|
||||
} else {
|
||||
logger.warn("Freebox Player Action service ThingHandler is null");
|
||||
}
|
||||
}
|
||||
|
||||
public static void reboot(ThingActions actions) {
|
||||
((ActivePlayerActions) actions).reboot();
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.action;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.handler.CallHandler;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {FreeplugActions} class is responsible to call corresponding actions on Freeplugs
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@ThingActionsScope(name = "freeboxos")
|
||||
@NonNullByDefault
|
||||
public class CallActions implements ThingActions {
|
||||
private final Logger logger = LoggerFactory.getLogger(CallActions.class);
|
||||
private @Nullable CallHandler handler;
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof CallHandler callHandler) {
|
||||
this.handler = callHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@RuleAction(label = "clear call queue", description = "Delete all call logged in the queue")
|
||||
public void reset() {
|
||||
logger.debug("Call log clear called");
|
||||
CallHandler localHandler = handler;
|
||||
if (localHandler != null) {
|
||||
localHandler.emptyQueue();
|
||||
} else {
|
||||
logger.warn("Call Action service ThingHandler is null");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.action;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.handler.FreeplugHandler;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {FreeplugActions} class is responsible to call corresponding actions on Freeplugs
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@ThingActionsScope(name = "freeboxos")
|
||||
@NonNullByDefault
|
||||
public class FreeplugActions implements ThingActions {
|
||||
private final Logger logger = LoggerFactory.getLogger(FreeplugActions.class);
|
||||
private @Nullable FreeplugHandler handler;
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof FreeplugHandler plugHandler) {
|
||||
this.handler = plugHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@RuleAction(label = "reset freeplug", description = "Resets the Freeplug")
|
||||
public void reset() {
|
||||
logger.debug("Freeplug reset requested");
|
||||
FreeplugHandler plugHandler = this.handler;
|
||||
if (plugHandler != null) {
|
||||
plugHandler.reset();
|
||||
} else {
|
||||
logger.warn("Freeplug Action service ThingHandler is null");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.action;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.handler.HostHandler;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {HostActions} class is responsible to call corresponding actions on a given lan host
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@ThingActionsScope(name = "freeboxos")
|
||||
@NonNullByDefault
|
||||
public class HostActions implements ThingActions {
|
||||
private final Logger logger = LoggerFactory.getLogger(HostActions.class);
|
||||
private @Nullable HostHandler handler;
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof HostHandler hostHandler) {
|
||||
this.handler = hostHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@RuleAction(label = "wol host", description = "Awakes a lan host")
|
||||
public void wol() {
|
||||
logger.debug("Host WOL called");
|
||||
HostHandler hostHandler = this.handler;
|
||||
if (hostHandler != null) {
|
||||
hostHandler.wol();
|
||||
} else {
|
||||
logger.warn("LanHost Action service ThingHandler is null");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.action;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.handler.PlayerHandler;
|
||||
import org.openhab.core.automation.annotation.ActionInput;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {PlayerActions} class is responsible to call corresponding actions on Freebox Player
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@ThingActionsScope(name = "freeboxos")
|
||||
@NonNullByDefault
|
||||
public class PlayerActions implements ThingActions {
|
||||
private final Logger logger = LoggerFactory.getLogger(PlayerActions.class);
|
||||
protected @Nullable PlayerHandler handler;
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof PlayerHandler playerHandler) {
|
||||
this.handler = playerHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@RuleAction(label = "send a key to player", description = "Sends a given key to the player")
|
||||
public void sendKey(@ActionInput(name = "key") String key) {
|
||||
logger.debug("Sending key {} to player", key);
|
||||
PlayerHandler playerHandler = this.handler;
|
||||
if (playerHandler != null) {
|
||||
playerHandler.sendKey(key, false, 1);
|
||||
} else {
|
||||
logger.warn("Freebox Player Action service ThingHandler is null");
|
||||
}
|
||||
}
|
||||
|
||||
@RuleAction(label = "send a long key to player", description = "Sends a given key to the player and keep it pressed")
|
||||
public void sendLongKey(@ActionInput(name = "key") String key) {
|
||||
logger.debug("Sending long press key {} to player", key);
|
||||
PlayerHandler playerHandler = this.handler;
|
||||
if (playerHandler != null) {
|
||||
playerHandler.sendKey(key, true, 1);
|
||||
} else {
|
||||
logger.warn("Freebox Player Action service ThingHandler is null");
|
||||
}
|
||||
}
|
||||
|
||||
@RuleAction(label = "send multiple keys to player", description = "Sends multiple keys to the player, comma separated")
|
||||
public void sendMultipleKeys(@ActionInput(name = "keys") String keys) {
|
||||
logger.debug("Sending keys {} to player", keys);
|
||||
PlayerHandler playerHandler = this.handler;
|
||||
if (playerHandler != null) {
|
||||
playerHandler.sendMultipleKeys(keys);
|
||||
} else {
|
||||
logger.warn("Freebox Player Action service ThingHandler is null");
|
||||
}
|
||||
}
|
||||
|
||||
@RuleAction(label = "send repeating key to player", description = "Sends a given key multiple times to the player")
|
||||
public void sendKeyRepeat(@ActionInput(name = "key") String key, @ActionInput(name = "count") int count) {
|
||||
logger.debug("Sending key {} to player {} times", key, count);
|
||||
PlayerHandler playerHandler = this.handler;
|
||||
if (playerHandler != null) {
|
||||
playerHandler.sendKey(key, false, count);
|
||||
} else {
|
||||
logger.warn("Freebox Player Action service ThingHandler is null");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.action;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.handler.RepeaterHandler;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {RepeaterActions} class is responsible to call corresponding actions on Freebox Repeater
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@ThingActionsScope(name = "freeboxos")
|
||||
@NonNullByDefault
|
||||
public class RepeaterActions implements ThingActions {
|
||||
private final Logger logger = LoggerFactory.getLogger(RepeaterActions.class);
|
||||
private @Nullable RepeaterHandler handler;
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof RepeaterHandler repeaterHandler) {
|
||||
this.handler = repeaterHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@RuleAction(label = "reboot free repeater", description = "Reboots the Free Repeater")
|
||||
public void reboot() {
|
||||
logger.debug("Repeater reboot called");
|
||||
RepeaterHandler localHandler = this.handler;
|
||||
if (localHandler != null) {
|
||||
localHandler.reboot();
|
||||
} else {
|
||||
logger.warn("Repeater Action service ThingHandler is null");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.action;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.handler.ServerHandler;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {ServerActions} class is responsible to call corresponding actions on Freebox Server
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@ThingActionsScope(name = "freeboxos")
|
||||
@NonNullByDefault
|
||||
public class ServerActions implements ThingActions {
|
||||
private final Logger logger = LoggerFactory.getLogger(ServerActions.class);
|
||||
private @Nullable ServerHandler handler;
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof ServerHandler serverHandler) {
|
||||
this.handler = serverHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@RuleAction(label = "reboot freebox server", description = "Reboots the Freebox Server")
|
||||
public void reboot() {
|
||||
logger.debug("Server reboot called");
|
||||
ServerHandler serverHandler = this.handler;
|
||||
if (serverHandler != null) {
|
||||
serverHandler.reboot();
|
||||
} else {
|
||||
logger.warn("Freebox Action service ThingHandler is null");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpStatus.Code;
|
||||
import org.openhab.binding.freeboxos.internal.api.deserialization.ForegroundAppDeserializer;
|
||||
import org.openhab.binding.freeboxos.internal.api.deserialization.ListDeserializer;
|
||||
import org.openhab.binding.freeboxos.internal.api.deserialization.StrictEnumTypeAdapterFactory;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.ForegroundApp;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
|
||||
import inet.ipaddr.IPAddress;
|
||||
import inet.ipaddr.IPAddressString;
|
||||
import inet.ipaddr.MACAddressString;
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link ApiHandler} is responsible for sending requests toward a given url and transform the answer in appropriate
|
||||
* DTO.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ApiHandler {
|
||||
public static final String AUTH_HEADER = "X-Fbx-App-Auth";
|
||||
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
|
||||
private static final String CONTENT_TYPE = "application/json; charset=" + DEFAULT_CHARSET.name();
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ApiHandler.class);
|
||||
private final HttpClient httpClient;
|
||||
private final Gson gson;
|
||||
|
||||
private long timeoutInMs = TimeUnit.SECONDS.toMillis(8);
|
||||
|
||||
public ApiHandler(HttpClient httpClient, TimeZoneProvider timeZoneProvider) {
|
||||
this.httpClient = httpClient;
|
||||
this.gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
.registerTypeAdapter(ZonedDateTime.class,
|
||||
(JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> {
|
||||
long timestamp = json.getAsJsonPrimitive().getAsLong();
|
||||
Instant i = Instant.ofEpochSecond(timestamp);
|
||||
return ZonedDateTime.ofInstant(i, timeZoneProvider.getTimeZone());
|
||||
})
|
||||
.registerTypeAdapter(MACAddress.class,
|
||||
(JsonDeserializer<MACAddress>) (json, type,
|
||||
jsonDeserializationContext) -> new MACAddressString(json.getAsString()).getAddress())
|
||||
.registerTypeAdapter(IPAddress.class,
|
||||
(JsonDeserializer<IPAddress>) (json, type,
|
||||
jsonDeserializationContext) -> new IPAddressString(json.getAsString()).getAddress())
|
||||
.registerTypeAdapter(ForegroundApp.class, new ForegroundAppDeserializer())
|
||||
.registerTypeAdapter(List.class, new ListDeserializer()).serializeNulls()
|
||||
.registerTypeAdapterFactory(new StrictEnumTypeAdapterFactory()).create();
|
||||
}
|
||||
|
||||
public synchronized <T> T executeUri(URI uri, HttpMethod method, Class<T> clazz, @Nullable String sessionToken,
|
||||
@Nullable Object payload) throws FreeboxException, InterruptedException {
|
||||
logger.debug("executeUrl {}: {} ", method, uri);
|
||||
|
||||
Request request = httpClient.newRequest(uri).method(method).timeout(timeoutInMs, TimeUnit.MILLISECONDS)
|
||||
.header(HttpHeader.CONTENT_TYPE, CONTENT_TYPE);
|
||||
|
||||
if (sessionToken != null) {
|
||||
request.header(AUTH_HEADER, sessionToken);
|
||||
}
|
||||
|
||||
if (payload != null) {
|
||||
request.content(new StringContentProvider(serialize(payload), DEFAULT_CHARSET), null);
|
||||
}
|
||||
|
||||
try {
|
||||
ContentResponse response = request.send();
|
||||
|
||||
Code statusCode = HttpStatus.getCode(response.getStatus());
|
||||
|
||||
if (statusCode != Code.OK && statusCode != Code.FORBIDDEN) {
|
||||
throw new FreeboxException(statusCode.getMessage());
|
||||
}
|
||||
|
||||
String content = new String(response.getContent(), DEFAULT_CHARSET);
|
||||
T result = deserialize(clazz, content);
|
||||
logger.trace("executeUrl {} - {} returned {}", method, uri, content);
|
||||
|
||||
if (statusCode == Code.OK) {
|
||||
return result;
|
||||
} else if (statusCode == Code.FORBIDDEN) {
|
||||
logger.debug("Fobidden, serviceReponse was {}, ", content);
|
||||
if (result instanceof Response<?> errorResponse) {
|
||||
throw new FreeboxException(errorResponse.getErrorCode(), errorResponse.getMsg());
|
||||
}
|
||||
}
|
||||
|
||||
throw new FreeboxException("Error '%s' requesting: %s", statusCode.getMessage(), uri.toString());
|
||||
} catch (TimeoutException | ExecutionException e) {
|
||||
throw new FreeboxException(e, "Exception while calling %s", request.getURI());
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T deserialize(Class<T> clazz, String json) {
|
||||
@Nullable
|
||||
T result = gson.fromJson(json, clazz);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
throw new IllegalArgumentException("Null result deserializing '%s', please file a bug report.".formatted(json));
|
||||
}
|
||||
|
||||
public String serialize(Object payload) {
|
||||
return gson.toJson(payload);
|
||||
}
|
||||
|
||||
public HttpClient getHttpClient() {
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
public void setTimeout(long millis) {
|
||||
timeoutInMs = millis;
|
||||
logger.debug("Timeout set to {} ms", timeoutInMs);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response.ErrorCode;
|
||||
|
||||
/**
|
||||
* Exception for errors when using the Freebox API
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FreeboxException extends Exception {
|
||||
private static final long serialVersionUID = 9197365222439228186L;
|
||||
private ErrorCode errorCode = ErrorCode.NONE;
|
||||
|
||||
public FreeboxException(String format, Object... args) {
|
||||
super(String.format(format, args));
|
||||
}
|
||||
|
||||
public FreeboxException(Exception cause, String format, Object... args) {
|
||||
super(String.format(format, args), cause);
|
||||
}
|
||||
|
||||
public FreeboxException(ErrorCode errorCode, String message) {
|
||||
this(message);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public ErrorCode getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpStatus.Code;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.ui.icon.AbstractResourceIconProvider;
|
||||
import org.openhab.core.ui.icon.IconProvider;
|
||||
import org.openhab.core.ui.icon.IconSet;
|
||||
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 {@FreeboxOsIconProvider} delivers icons provided by FreeboxOS
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(immediate = true, service = { IconProvider.class })
|
||||
public class FreeboxOsIconProvider extends AbstractResourceIconProvider {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FreeboxOsIconProvider.class);
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private final UriBuilder uriBuilder;
|
||||
|
||||
@Activate
|
||||
public FreeboxOsIconProvider(final @Reference TranslationProvider i18nProvider,
|
||||
final @Reference HttpClientFactory httpClientFactory) {
|
||||
super(i18nProvider);
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.uriBuilder = UriBuilder.fromPath("/").scheme("http").host(FreeboxTlsCertificateProvider.DEFAULT_NAME)
|
||||
.path("resources/images/home/pictos");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<IconSet> getIconSets(@Nullable Locale locale) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer getPriority() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable InputStream getResource(String iconSetId, String resourceName) {
|
||||
URI uri = uriBuilder.clone().path(resourceName).build();
|
||||
Request request = httpClient.newRequest(uri).method(HttpMethod.GET);
|
||||
|
||||
try {
|
||||
ContentResponse response = request.send();
|
||||
if (HttpStatus.getCode(response.getStatus()) == Code.OK) {
|
||||
return new ByteArrayInputStream(response.getContent());
|
||||
}
|
||||
} catch (InterruptedException | TimeoutException | ExecutionException e) {
|
||||
logger.warn("Error getting icon {}: {}", resourceName, e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasResource(String iconSetId, String resourceName) {
|
||||
return resourceName.contains(".png") && getResource(iconSetId, resourceName) != null;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api;
|
||||
|
||||
import java.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 CertificateManager for the Freebox SSL certificate
|
||||
*
|
||||
* @author Gaël L'hopital - Initial Contribution
|
||||
*/
|
||||
@Component
|
||||
@NonNullByDefault
|
||||
public class FreeboxTlsCertificateProvider implements TlsCertificateProvider {
|
||||
|
||||
private static final String CERTIFICATE_NAME = "freeboxECCRootCA.crt";
|
||||
|
||||
public static final String DEFAULT_NAME = "mafreebox.freebox.fr";
|
||||
|
||||
@Override
|
||||
public String getHostName() {
|
||||
return DEFAULT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getCertificate() {
|
||||
URL resource = Thread.currentThread().getContextClassLoader().getResource(CERTIFICATE_NAME);
|
||||
if (resource != null) {
|
||||
return resource;
|
||||
}
|
||||
throw new IllegalStateException("Certificate '%s' not found or not accessible".formatted(CERTIFICATE_NAME));
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api;
|
||||
|
||||
import java.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 CertificateManager for the IliadBox SSL certificate
|
||||
*
|
||||
* @author Gaël L'hopital - Initial Contribution
|
||||
*/
|
||||
@Component
|
||||
@NonNullByDefault
|
||||
public class IliadboxTlsCertificateProvider implements TlsCertificateProvider {
|
||||
|
||||
private static final String CERTIFICATE_NAME = "iliadboxECCRootCA.crt";
|
||||
|
||||
public static final String DEFAULT_NAME = "myiliadbox.iliad.it";
|
||||
|
||||
@Override
|
||||
public String getHostName() {
|
||||
return DEFAULT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getCertificate() {
|
||||
URL resource = Thread.currentThread().getContextClassLoader().getResource(CERTIFICATE_NAME);
|
||||
if (resource != null) {
|
||||
return resource;
|
||||
}
|
||||
throw new IllegalStateException("Certificate '%s' not found or not accessible".formatted(CERTIFICATE_NAME));
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LoginManager;
|
||||
|
||||
/**
|
||||
* Exception for errors when Session require missing permission
|
||||
*
|
||||
* @author ben12 - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PermissionException extends FreeboxException {
|
||||
private static final long serialVersionUID = 3965810786699311126L;
|
||||
|
||||
private final LoginManager.Permission permission;
|
||||
|
||||
public PermissionException(LoginManager.Permission permission, String format, Object... args) {
|
||||
super(format, args);
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
public LoginManager.Permission getPermission() {
|
||||
return permission;
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LoginManager;
|
||||
|
||||
/**
|
||||
* Defines an API result that returns a single object
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Response<ResultType> {
|
||||
public static enum ErrorCode {
|
||||
AUTH_REQUIRED,
|
||||
BAD_LOGIN,
|
||||
TOO_SHORT,
|
||||
IN_DICTIONNARY,
|
||||
BAD_XKCD,
|
||||
NOT_ENOUGH_DIFFERENT_CHARS,
|
||||
INVALID_TOKEN,
|
||||
PENDING_TOKEN,
|
||||
INSUFFICIENT_RIGHTS,
|
||||
DENIED_FROM_EXTERNAL_IP,
|
||||
INVALID_REQUEST,
|
||||
RATELIMITED,
|
||||
NEW_APPS_DENIED,
|
||||
APPS_AUTHORIZATION_DENIED,
|
||||
APPS_AUTHORIZATION_TIMEOUT,
|
||||
PASSWORD_RESET_DENIED,
|
||||
APPS_DENIED,
|
||||
INTERNAL_ERROR,
|
||||
SERVICE_DOWN,
|
||||
DISK_FULL,
|
||||
OP_FAILED,
|
||||
DISK_BUSY,
|
||||
ARRAY_START_FAILED,
|
||||
ARRAY_STOP_FAILED,
|
||||
ARRAY_NOT_FOUND,
|
||||
INVAL,
|
||||
NODEV,
|
||||
NOENT,
|
||||
NETDOWN,
|
||||
BUSY,
|
||||
INVALID_PORT,
|
||||
INSECURE_PASSWORD,
|
||||
INVALID_PROVIDER,
|
||||
INVALID_NEXT_HOP,
|
||||
INVALID_API_VERSION,
|
||||
INVAL_WPS_MACFILTER,
|
||||
INVAL_WPS_NEEDS_CCMP,
|
||||
INVALID_ID,
|
||||
PATH_NOT_FOUND,
|
||||
ACCESS_DENIED,
|
||||
DESTINATION_CONFLICT,
|
||||
CANCELLED,
|
||||
TASK_NOT_FOUND,
|
||||
HTTP,
|
||||
INVALID_URL,
|
||||
INVALID_OPERATION,
|
||||
INVALID_FILE,
|
||||
CTX_FILE_ERROR,
|
||||
HIBERNATING,
|
||||
TOO_MANY_TASKS,
|
||||
EXISTS,
|
||||
EXIST,
|
||||
CONNECTION_REFUSED,
|
||||
NO_FREEBOX,
|
||||
ALREADY_AUTHORIZED,
|
||||
ECRC,
|
||||
ERR_001,
|
||||
ERR_002,
|
||||
ERR_003,
|
||||
ERR_004,
|
||||
ERR_005,
|
||||
ERR_009,
|
||||
ERR_010,
|
||||
ERR_030,
|
||||
ERR_031,
|
||||
NONE,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private ErrorCode errorCode = ErrorCode.NONE;
|
||||
private LoginManager.Permission missingRight = LoginManager.Permission.NONE;
|
||||
private String msg = "";
|
||||
private List<ResultType> result = List.of();
|
||||
private boolean success;
|
||||
|
||||
// In some cases I did not understand deserialization can still produce null result
|
||||
@SuppressWarnings("null")
|
||||
public List<ResultType> getResult() {
|
||||
List<ResultType> localResult = result;
|
||||
return localResult != null ? localResult : List.of();
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public LoginManager.Permission getMissingRight() {
|
||||
return missingRight;
|
||||
}
|
||||
|
||||
public ErrorCode getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.deserialization;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.ForegroundApp;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.PlayerContext;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.TvContext;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
/**
|
||||
* Custom deserializer to handle {@link ForegroundApp} object
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ForegroundAppDeserializer implements JsonDeserializer<ForegroundApp> {
|
||||
|
||||
@Override
|
||||
public @NonNull ForegroundApp deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
Object obj;
|
||||
|
||||
String thePackage = json.getAsJsonObject().get("package").getAsString();
|
||||
JsonElement jsonElement2 = json.getAsJsonObject().get("context");
|
||||
if (jsonElement2 == null) {
|
||||
obj = null;
|
||||
} else if ("fr.freebox.tv".equals(thePackage)) {
|
||||
obj = context.deserialize(jsonElement2, TvContext.class);
|
||||
} else {
|
||||
obj = context.deserialize(jsonElement2, PlayerContext.class);
|
||||
}
|
||||
|
||||
int packageId = json.getAsJsonObject().get("package_id").getAsInt();
|
||||
String curlUrl = json.getAsJsonObject().get("cur_url").getAsString();
|
||||
Objects.requireNonNull(thePackage);
|
||||
return new ForegroundApp(packageId, curlUrl, obj, thePackage);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.deserialization;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
/**
|
||||
* The {@link ListDeserializer} is a specialized deserializer aimed to transform a null object, a single object or
|
||||
* a list of objects into a list containing 0, 1 or n elements.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ListDeserializer implements JsonDeserializer<List<?>> {
|
||||
|
||||
@Override
|
||||
public @NonNull List<?> deserialize(@Nullable JsonElement json, @Nullable Type clazz,
|
||||
@Nullable JsonDeserializationContext context) throws JsonParseException {
|
||||
if (json != null && clazz != null && context != null) {
|
||||
JsonArray jsonArray = toJsonArray(json);
|
||||
ArrayList<?> result = new ArrayList<>(jsonArray != null ? jsonArray.size() : 0);
|
||||
|
||||
if (jsonArray != null) {
|
||||
Type[] typeArguments = ((ParameterizedType) clazz).getActualTypeArguments();
|
||||
if (typeArguments.length > 0) {
|
||||
Type objectType = typeArguments[0];
|
||||
for (int i = 0; i < jsonArray.size(); i++) {
|
||||
result.add(context.deserialize(jsonArray.get(i), objectType));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
private @Nullable JsonArray toJsonArray(JsonElement json) {
|
||||
if (json instanceof JsonArray) {
|
||||
return json.getAsJsonArray();
|
||||
} else if (json instanceof JsonObject) {
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
if (json.getAsJsonObject().size() > 0) {
|
||||
jsonArray.add(json);
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.deserialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Enforces a fallback to UNKNOWN when deserializing enum types, marked as @NonNull whereas they were valued
|
||||
* to null if the appropriate value is absent.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class StrictEnumTypeAdapterFactory implements TypeAdapterFactory {
|
||||
private static final StringReader UNKNOWN = new StringReader("\"UNKNOWN\"");
|
||||
|
||||
@Override
|
||||
public @Nullable <T> TypeAdapter<T> create(@NonNullByDefault({}) Gson gson,
|
||||
@NonNullByDefault({}) TypeToken<T> type) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<T> rawType = (Class<T>) type.getRawType();
|
||||
return rawType.isEnum() ? newStrictEnumAdapter(gson.getDelegateAdapter(this, type)) : null;
|
||||
}
|
||||
|
||||
private <T> TypeAdapter<T> newStrictEnumAdapter(TypeAdapter<T> delegateAdapter) {
|
||||
return new TypeAdapter<T>() {
|
||||
@Override
|
||||
public void write(JsonWriter out, @Nullable T value) throws IOException {
|
||||
delegateAdapter.write(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull T read(JsonReader in) throws IOException {
|
||||
String searched = in.nextString().toUpperCase().replace("/", "_").replace("-", "_");
|
||||
JsonReader delegateReader = new JsonReader(new StringReader('"' + searched + '"'));
|
||||
@Nullable
|
||||
T value = delegateAdapter.read(delegateReader);
|
||||
delegateReader.close();
|
||||
if (value == null) {
|
||||
UNKNOWN.reset();
|
||||
value = delegateAdapter.read(new JsonReader(UNKNOWN));
|
||||
}
|
||||
return Objects.requireNonNull(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
|
||||
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link APManager} is the Java class used to handle api requests related to wifi access points
|
||||
* provided by the Freebox Server
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class APManager extends ListableRest<APManager.WifiAp, APManager.APResponse> {
|
||||
private static final String PATH = "ap";
|
||||
private static final String STATIONS_PATH = "stations";
|
||||
|
||||
protected static record WifiInformation(String ssid, String band, int signal) { // Valid RSSI goes from -120 to 0
|
||||
}
|
||||
|
||||
public static record LanAccessPoint(String mac, String type, String uid, @Nullable String connectivityType,
|
||||
long rxBytes, // received bytes (from station to Freebox)
|
||||
long txBytes, // transmitted bytes (from Freebox to station)
|
||||
long txRate, // reception data rate (in bytes/s)
|
||||
long rxRate, // transmission data rate (in bytes/s)
|
||||
WifiInformation wifiInformation) {
|
||||
|
||||
public int getSignal() {
|
||||
return wifiInformation.signal();
|
||||
}
|
||||
|
||||
public @Nullable String getSsid() {
|
||||
return wifiInformation().ssid();
|
||||
}
|
||||
}
|
||||
|
||||
private static enum State {
|
||||
ASSOCIATED,
|
||||
AUTHENTICATED,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record Station(String id, MACAddress mac, String bssid, @Nullable String hostname, LanHost host,
|
||||
State state, int inactive, int connDuration, //
|
||||
long rxBytes, // received bytes (from station to Freebox)
|
||||
long txBytes, // transmitted bytes (from Freebox to station)
|
||||
long txRate, // reception data rate (in bytes/s)
|
||||
long rxRate, // transmission data rate (in bytes/s)
|
||||
int signal) { // signal attenuation (in dB)
|
||||
|
||||
public @Nullable String getSsid() {
|
||||
LanAccessPoint accessPoint = host.accessPoint();
|
||||
return accessPoint != null ? accessPoint.getSsid() : null;
|
||||
}
|
||||
|
||||
public @Nullable ZonedDateTime getLastSeen() {
|
||||
return host.getLastSeen();
|
||||
}
|
||||
}
|
||||
|
||||
protected static record ApStatus(ApState state, int channelWidth, int primaryChannel, int secondaryChannel,
|
||||
int dfsCacRemainingTime, boolean dfsDisabled) {
|
||||
private static enum ApState {
|
||||
SCANNING, // Ap is probing wifi channels
|
||||
NO_PARAM, // Ap is not configured
|
||||
BAD_PARAM, // Ap has an invalid configuration
|
||||
DISABLED, // Ap is permanently disabled
|
||||
DISABLED_PLANNING, // Ap is currently disabled according to planning
|
||||
NO_ACTIVE_BSS, // Ap has no active BSS
|
||||
STARTING, // Ap is starting
|
||||
ACS, // Ap is selecting the best available channel
|
||||
HT_SCAN, // Ap is scanning for other access point
|
||||
DFS, // Ap is performing dynamic frequency selection
|
||||
ACTIVE, // Ap is active
|
||||
FAILED, // Ap has failed to start
|
||||
UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
protected static record WifiAp(int id, String name, ApStatus status) {
|
||||
}
|
||||
|
||||
private class ApHostsResponse extends Response<Station> {
|
||||
}
|
||||
|
||||
protected class APResponse extends Response<WifiAp> {
|
||||
}
|
||||
|
||||
public APManager(FreeboxOsSession session, UriBuilder uriBuilder) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, APResponse.class, uriBuilder.path(PATH));
|
||||
}
|
||||
|
||||
private List<Station> getApStations(int apId) throws FreeboxException {
|
||||
return get(ApHostsResponse.class, Integer.toString(apId), STATIONS_PATH);
|
||||
}
|
||||
|
||||
public List<Station> getStations() throws FreeboxException {
|
||||
List<Station> hosts = new ArrayList<>();
|
||||
for (WifiAp ap : getDevices()) {
|
||||
hosts.addAll(getApStations(ap.id));
|
||||
}
|
||||
return hosts;
|
||||
}
|
||||
|
||||
public Optional<Station> getStation(MACAddress mac) throws FreeboxException {
|
||||
return getStations().stream().filter(host -> host.mac().equals(mac)).findFirst();
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* The {@link AfpManager} is the Java class used to handle api requests related to Afp shares
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AfpManager extends ConfigurableRest<AfpManager.Afp, AfpManager.ConfigResponse> {
|
||||
private static final String AFP_PATH = "afp";
|
||||
|
||||
protected static class ConfigResponse extends Response<Afp> {
|
||||
}
|
||||
|
||||
protected static record Afp(boolean enabled, boolean guestAllow, ServerType serverType, @Nullable String loginName,
|
||||
@Nullable String loginPassword) {
|
||||
private static enum ServerType {
|
||||
POWERBOOK,
|
||||
POWERMAC,
|
||||
MACMINI,
|
||||
IMAC,
|
||||
MACBOOK,
|
||||
MACBOOKPRO,
|
||||
MACBOOKAIR,
|
||||
MACPRO,
|
||||
APPLETV,
|
||||
AIRPORT,
|
||||
XSERVE,
|
||||
UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public AfpManager(FreeboxOsSession session, UriBuilder uriBuilder) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, ConfigResponse.class, uriBuilder.path(AFP_PATH), null);
|
||||
}
|
||||
|
||||
public boolean getStatus() throws FreeboxException {
|
||||
return getConfig().enabled;
|
||||
}
|
||||
|
||||
public boolean setStatus(boolean enabled) throws FreeboxException {
|
||||
Afp config = getConfig();
|
||||
Afp newConfig = new Afp(enabled, config.guestAllow, config.serverType, config.loginName, config.loginPassword);
|
||||
return setConfig(newConfig).enabled;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* The {@link AirMediaManager} is the Java class used to handle api requests related to air media global configuration
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AirMediaManager extends ConfigurableRest<AirMediaManager.Config, AirMediaManager.ConfigResponse> {
|
||||
private static final String PATH = "airmedia";
|
||||
|
||||
protected static record Config(boolean enabled) {
|
||||
}
|
||||
|
||||
protected static class ConfigResponse extends Response<Config> {
|
||||
}
|
||||
|
||||
public AirMediaManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, ConfigResponse.class, session.getUriBuilder().path(PATH),
|
||||
CONFIG_PATH);
|
||||
session.addManager(MediaReceiverManager.class, new MediaReceiverManager(session, getUriBuilder()));
|
||||
}
|
||||
|
||||
public boolean getStatus() throws FreeboxException {
|
||||
return getConfig().enabled();
|
||||
}
|
||||
|
||||
public boolean setStatus(boolean enabled) throws FreeboxException {
|
||||
return setConfig(new Config(enabled)).enabled();
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.THING_CALL;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* The {@link CallManager} is the Java class used to handle api requests related to calls
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CallManager extends RestManager {
|
||||
private static final String LOG_SUB_PATH = "log/";
|
||||
private static final String DELETE_ACTION = "delete_all";
|
||||
|
||||
private static class Calls extends Response<Call> {
|
||||
}
|
||||
|
||||
public static enum Type {
|
||||
ACCEPTED,
|
||||
MISSED,
|
||||
OUTGOING,
|
||||
INCOMING,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record Call(Type type, //
|
||||
ZonedDateTime datetime, // Call creation timestamp.
|
||||
String number, // Calling or called number
|
||||
int duration, // Call duration in seconds.
|
||||
String name) {
|
||||
|
||||
public @Nullable String name() {
|
||||
return name.equals(number) ? null : name;
|
||||
}
|
||||
}
|
||||
|
||||
public CallManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.CALLS, session.getUriBuilder().path(THING_CALL));
|
||||
}
|
||||
|
||||
// Retrieves a sorted list of all call entries
|
||||
public List<Call> getCallEntries() throws FreeboxException {
|
||||
List<Call> callList = new ArrayList<>(
|
||||
get(Calls.class, LOG_SUB_PATH).stream().sorted(Comparator.comparing(Call::datetime)).toList());
|
||||
Call last = callList.get(callList.size() - 1);
|
||||
// The INCOMING type call can only be set on the last call if its duration is 0;
|
||||
if (last.type == Type.MISSED && last.duration == 0) {
|
||||
callList.remove(callList.size() - 1);
|
||||
callList.add(new Call(Type.INCOMING, last.datetime, last.number, 0, last.name));
|
||||
}
|
||||
return callList;
|
||||
}
|
||||
|
||||
public void emptyQueue() throws FreeboxException {
|
||||
post(LOG_SUB_PATH, DELETE_ACTION);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* The {@link ConfigurableRest} is the Java class used to handle portions of the Api that accept to get and set
|
||||
* configuration based on a given DTO
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ConfigurableRest<T, Y extends Response<T>> extends RestManager {
|
||||
protected static final String CONFIG_PATH = "config";
|
||||
|
||||
private final Class<Y> responseClazz;
|
||||
private final @Nullable String configPath;
|
||||
|
||||
protected ConfigurableRest(FreeboxOsSession session, LoginManager.Permission required, Class<Y> responseClazz,
|
||||
UriBuilder uri, @Nullable String configPath) throws FreeboxException {
|
||||
super(session, required, uri);
|
||||
this.responseClazz = responseClazz;
|
||||
this.configPath = configPath;
|
||||
}
|
||||
|
||||
public T getConfig() throws FreeboxException {
|
||||
return configPath != null ? getSingle(responseClazz, configPath) : getSingle(responseClazz);
|
||||
}
|
||||
|
||||
protected T setConfig(T config) throws FreeboxException {
|
||||
return configPath != null ? put(responseClazz, config, configPath) : put(responseClazz, config);
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
import inet.ipaddr.IPAddress;
|
||||
|
||||
/**
|
||||
* The {@link ConnectionManager} is the Java class used to handle api requests related to connection
|
||||
*
|
||||
* https://dev.freebox.fr/sdk/os/system/#
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ConnectionManager extends ConfigurableRest<ConnectionManager.Status, ConnectionManager.StatusResponse> {
|
||||
private static final String PATH = "connection";
|
||||
|
||||
protected static class StatusResponse extends Response<Status> {
|
||||
}
|
||||
|
||||
private static enum State {
|
||||
GOING_UP,
|
||||
UP,
|
||||
GOING_DOWN,
|
||||
DOWN,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private static enum Type {
|
||||
ETHERNET,
|
||||
RFC2684,
|
||||
PPPOATM,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private static enum Media {
|
||||
FTTH,
|
||||
ETHERNET,
|
||||
XDSL,
|
||||
BACKUP_4G,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record Status(State state, Type type, Media media, @Nullable List<Integer> ipv4PortRange,
|
||||
@Nullable IPAddress ipv4, // This can be null if state is not up
|
||||
@Nullable IPAddress ipv6, // This can be null if state is not up
|
||||
long rateUp, // current upload rate in byte/s
|
||||
long rateDown, // current download rate in byte/s
|
||||
long bandwidthUp, // available upload bandwidth in bit/s
|
||||
long bandwidthDown, // available download bandwidth in bit/s
|
||||
long bytesUp, // total uploaded bytes since last connection
|
||||
long bytesDown // total downloaded bytes since last connection
|
||||
) {
|
||||
}
|
||||
|
||||
public ConnectionManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, StatusResponse.class, session.getUriBuilder().path(PATH), null);
|
||||
}
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.openhab.binding.freeboxos.internal.api.ApiHandler;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.PermissionException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response.ErrorCode;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LoginManager.Session;
|
||||
import org.openhab.binding.freeboxos.internal.config.FreeboxOsConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link FreeboxOsSession} is responsible for sending requests toward a given url and transform the answer in
|
||||
* appropriate dto.
|
||||
*
|
||||
* @author Gaël L'Hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FreeboxOsSession {
|
||||
private static final String API_VERSION_PATH = "api_version";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FreeboxOsSession.class);
|
||||
private final Map<Class<? extends RestManager>, RestManager> restManagers = new HashMap<>();
|
||||
private final ApiHandler apiHandler;
|
||||
|
||||
private @NonNullByDefault({}) UriBuilder uriBuilder;
|
||||
private @Nullable Session session;
|
||||
private String appToken = "";
|
||||
|
||||
public static enum BoxModel {
|
||||
FBXGW_R1_FULL, // Freebox Server (v6) revision 1
|
||||
FBXGW_R2_FULL, // Freebox Server (v6) revision 2
|
||||
FBXGW_R1_MINI, // Freebox Mini revision 1
|
||||
FBXGW_R2_MINI, // Freebox Mini revision 2
|
||||
FBXGW_R1_ONE, // Freebox One revision 1
|
||||
FBXGW_R2_ONE, // Freebox One revision 2
|
||||
FBXGW7_R1_FULL, // Freebox v7 revision 1
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record ApiVersion(String apiBaseUrl, @Nullable String apiDomain, String apiVersion, BoxModel boxModel,
|
||||
@Nullable String boxModelName, String deviceName, String deviceType, boolean httpsAvailable, int httpsPort,
|
||||
String uid) {
|
||||
|
||||
/**
|
||||
* @return a string like eg: '/api/v8'
|
||||
*/
|
||||
private String baseUrl() {
|
||||
return "%sv%s".formatted(apiBaseUrl, apiVersion.split("\\.")[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public FreeboxOsSession(ApiHandler apiHandler) {
|
||||
this.apiHandler = apiHandler;
|
||||
}
|
||||
|
||||
public void initialize(FreeboxOsConfiguration config) throws FreeboxException, InterruptedException {
|
||||
ApiVersion version = apiHandler.executeUri(config.getUriBuilder(API_VERSION_PATH).build(), HttpMethod.GET,
|
||||
ApiVersion.class, null, null);
|
||||
this.uriBuilder = config.getUriBuilder(version.baseUrl());
|
||||
getManager(LoginManager.class);
|
||||
getManager(NetShareManager.class);
|
||||
getManager(LanManager.class);
|
||||
getManager(WifiManager.class);
|
||||
getManager(FreeplugManager.class);
|
||||
getManager(AirMediaManager.class);
|
||||
}
|
||||
|
||||
public void openSession(String appToken) throws FreeboxException {
|
||||
Session newSession = getManager(LoginManager.class).openSession(appToken);
|
||||
getManager(WebSocketManager.class).openSession(newSession.sessionToken());
|
||||
session = newSession;
|
||||
this.appToken = appToken;
|
||||
}
|
||||
|
||||
public String grant() throws FreeboxException {
|
||||
return getManager(LoginManager.class).checkGrantStatus();
|
||||
}
|
||||
|
||||
public void closeSession() {
|
||||
Session currentSession = session;
|
||||
if (currentSession != null) {
|
||||
try {
|
||||
getManager(WebSocketManager.class).closeSession();
|
||||
getManager(LoginManager.class).closeSession();
|
||||
session = null;
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error closing session: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
appToken = "";
|
||||
restManagers.clear();
|
||||
}
|
||||
|
||||
private synchronized <F, T extends Response<F>> List<F> execute(URI uri, HttpMethod method, Class<T> clazz,
|
||||
boolean retryAuth, int retryCount, @Nullable Object aPayload) throws FreeboxException {
|
||||
try {
|
||||
T response = apiHandler.executeUri(uri, method, clazz, getSessionToken(), aPayload);
|
||||
if (response.getErrorCode() == ErrorCode.INTERNAL_ERROR && retryCount > 0) {
|
||||
return execute(uri, method, clazz, false, retryCount - 1, aPayload);
|
||||
} else if (retryAuth && response.getErrorCode() == ErrorCode.AUTH_REQUIRED) {
|
||||
openSession(appToken);
|
||||
return execute(uri, method, clazz, false, retryCount, aPayload);
|
||||
}
|
||||
if (!response.isSuccess()) {
|
||||
throw new FreeboxException("Api request failed: %s", response.getMsg());
|
||||
}
|
||||
return response.getResult();
|
||||
} catch (FreeboxException e) {
|
||||
if (ErrorCode.AUTH_REQUIRED.equals(e.getErrorCode())) {
|
||||
openSession(appToken);
|
||||
return execute(uri, method, clazz, false, retryCount, aPayload);
|
||||
}
|
||||
throw e;
|
||||
} catch (InterruptedException ignored) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
public <F, T extends Response<F>> List<F> execute(URI uri, HttpMethod method, Class<T> clazz,
|
||||
@Nullable Object aPayload) throws FreeboxException {
|
||||
return execute(uri, method, clazz, getSessionToken() != null, 3, aPayload);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized <T extends RestManager> T getManager(Class<T> clazz) throws FreeboxException {
|
||||
RestManager manager = restManagers.get(clazz);
|
||||
if (manager == null) {
|
||||
try {
|
||||
Constructor<T> managerConstructor = clazz.getConstructor(FreeboxOsSession.class);
|
||||
manager = addManager(clazz, managerConstructor.newInstance(this));
|
||||
} catch (InvocationTargetException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof PermissionException) {
|
||||
throw (PermissionException) cause;
|
||||
}
|
||||
throw new FreeboxException(e, "Unable to call RestManager constructor for %s", clazz.getName());
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new FreeboxException(e, "Unable to call RestManager constructor for %s", clazz.getName());
|
||||
}
|
||||
}
|
||||
return (T) manager;
|
||||
}
|
||||
|
||||
public <T extends RestManager> T addManager(Class<T> clazz, T manager) {
|
||||
restManagers.put(clazz, manager);
|
||||
return manager;
|
||||
}
|
||||
|
||||
boolean hasPermission(LoginManager.Permission required) {
|
||||
Session currentSession = session;
|
||||
return currentSession != null ? currentSession.hasPermission(required) : false;
|
||||
}
|
||||
|
||||
private @Nullable String getSessionToken() {
|
||||
Session currentSession = session;
|
||||
return currentSession != null ? currentSession.sessionToken() : null;
|
||||
}
|
||||
|
||||
public UriBuilder getUriBuilder() {
|
||||
return uriBuilder.clone();
|
||||
}
|
||||
|
||||
public ApiHandler getApiHandler() {
|
||||
return apiHandler;
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.THING_FREEPLUG;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link FreeplugManager} is the Java class used to handle api requests related to freeplugs
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FreeplugManager extends RestManager {
|
||||
private static final String RESET_ACTION = "reset";
|
||||
|
||||
private static class Networks extends Response<Network> {
|
||||
}
|
||||
|
||||
public static enum NetRole {
|
||||
STA, // Freeplug station
|
||||
PCO, // Freeplug proxy coordinator
|
||||
CCO, // Central Coordinator
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private enum Status {
|
||||
UP,
|
||||
DOWN,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
public static record Freeplug(MACAddress id, String netId, // Id of the network holding the plug
|
||||
boolean local, // if true the Freeplug is connected directly to the Freebox
|
||||
NetRole netRole, // Freeplug network role
|
||||
String model, Status ethPortStatus, //
|
||||
boolean ethFullDuplex, // ethernet link is full duplex
|
||||
boolean hasNetwork, // is connected to the network
|
||||
int ethSpeed, // ethernet port speed
|
||||
int inactive, // seconds since last activity
|
||||
int rxRate, // rx rate (from the freeplugs to the “cco” freeplug) (in Mb/s) -1 if not available
|
||||
int txRate) { // tx rate (from the “cco” freeplug to the freeplugs) (in Mb/s) -1 if not available
|
||||
}
|
||||
|
||||
private static record Network(MACAddress id, List<Freeplug> members) {
|
||||
}
|
||||
|
||||
public FreeplugManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, session.getUriBuilder().path(THING_FREEPLUG));
|
||||
}
|
||||
|
||||
// Most of the users will host only one CPL network on their server, so we hide the network level in the manager
|
||||
public List<Freeplug> getPlugs() throws FreeboxException {
|
||||
return get(Networks.class).stream().map(Network::members).flatMap(List::stream).toList();
|
||||
}
|
||||
|
||||
public Optional<Freeplug> getPlug(MACAddress mac) throws FreeboxException {
|
||||
return getPlugs().stream().filter(plug -> plug.id.equals(mac)).findFirst();
|
||||
}
|
||||
|
||||
public void reboot(MACAddress mac) throws FreeboxException {
|
||||
post(mac.toColonDelimitedString(), RESET_ACTION);
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* The {@link FtpManager} is the Java class used to handle api requests related to ftp
|
||||
*
|
||||
* https://dev.freebox.fr/sdk/os/system/#
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FtpManager extends ConfigurableRest<FtpManager.Config, FtpManager.ConfigResponse> {
|
||||
private static final String PATH = "ftp";
|
||||
|
||||
protected static class ConfigResponse extends Response<Config> {
|
||||
}
|
||||
|
||||
protected static record Config(boolean enabled, boolean allowAnonymous, boolean allowAnonymousWrite,
|
||||
boolean allowRemoteAccess, boolean weakPassword, int portCtrl, int portData, String remoteDomain) {
|
||||
}
|
||||
|
||||
public FtpManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, ConfigResponse.class, session.getUriBuilder().path(PATH),
|
||||
CONFIG_PATH);
|
||||
}
|
||||
|
||||
public boolean getStatus() throws FreeboxException {
|
||||
return getConfig().enabled();
|
||||
}
|
||||
|
||||
public boolean setStatus(boolean enabled) throws FreeboxException {
|
||||
Config oldConfig = getConfig();
|
||||
Config newConfig = new Config(enabled, oldConfig.allowAnonymous, oldConfig.allowAnonymousWrite,
|
||||
oldConfig.allowRemoteAccess, oldConfig.weakPassword, oldConfig.portCtrl, oldConfig.portData,
|
||||
oldConfig.remoteDomain);
|
||||
return setConfig(newConfig).enabled();
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.BINDING_ID;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link HomeManager} is the Java class used to handle api requests related to home
|
||||
*
|
||||
* @author ben12 - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HomeManager extends RestManager {
|
||||
private static final String PATH = "home";
|
||||
private static final String NODES_PATH = "nodes";
|
||||
private static final String ENDPOINTS_PATH = "endpoints";
|
||||
|
||||
private static class EndpointStateResponse extends Response<EndpointState> {
|
||||
}
|
||||
|
||||
private static class HomeNodesResponse extends Response<HomeNode> {
|
||||
}
|
||||
|
||||
private static enum AccessType {
|
||||
R,
|
||||
W,
|
||||
RW,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private static enum DisplayType {
|
||||
TEXT,
|
||||
ICON,
|
||||
BUTTON,
|
||||
SLIDER,
|
||||
TOGGLE,
|
||||
COLOR,
|
||||
WARNING,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private static record EndpointValue<T>(T value) {
|
||||
}
|
||||
|
||||
private static record EndpointUi(AccessType access, DisplayType display, String iconUrl, @Nullable String unit) {
|
||||
}
|
||||
|
||||
private static enum ValueType {
|
||||
BOOL,
|
||||
INT,
|
||||
FLOAT,
|
||||
VOID,
|
||||
STRING,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record EndpointState(@Nullable String value, ValueType valueType, long refresh) {
|
||||
public boolean asBoolean() {
|
||||
String local = value;
|
||||
return local != null ? Boolean.valueOf(local) : false;
|
||||
}
|
||||
|
||||
public int asInt() {
|
||||
String local = value;
|
||||
return local != null ? Integer.valueOf(local) : Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
public @Nullable String value() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum EpType {
|
||||
SIGNAL,
|
||||
SLOT,
|
||||
UNKNOWN;
|
||||
|
||||
public String asConfId() {
|
||||
return name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
private static record LogEntry(long timestamp, int value) {
|
||||
}
|
||||
|
||||
public static record Endpoint(int id, String name, String label, EpType epType, Visibility visibility, int refresh,
|
||||
ValueType valueType, EndpointUi ui, @Nullable String category, Object value, List<LogEntry> history) {
|
||||
private static enum Visibility {
|
||||
INTERNAL,
|
||||
NORMAL,
|
||||
DASHBOARD,
|
||||
UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
private static enum Status {
|
||||
UNREACHABLE,
|
||||
DISABLED,
|
||||
ACTIVE,
|
||||
UNPAIRED,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static enum Category {
|
||||
BASIC_SHUTTER,
|
||||
SHUTTER,
|
||||
ALARM,
|
||||
KFB,
|
||||
CAMERA,
|
||||
UNKNOWN;
|
||||
|
||||
private final ThingTypeUID thingTypeUID;
|
||||
|
||||
Category() {
|
||||
thingTypeUID = new ThingTypeUID(BINDING_ID, name().toLowerCase());
|
||||
}
|
||||
|
||||
public ThingTypeUID getThingTypeUID() {
|
||||
return thingTypeUID;
|
||||
}
|
||||
}
|
||||
|
||||
public static record NodeType(@SerializedName("abstract") boolean _abstract, List<Endpoint> endpoints,
|
||||
boolean generic, String icon, String inherit, String label, String name, boolean physical) {
|
||||
}
|
||||
|
||||
public static record HomeNode(int id, @Nullable String name, @Nullable String label, Category category,
|
||||
Status status, List<Endpoint> showEndpoints, Map<String, String> props, NodeType type) {
|
||||
}
|
||||
|
||||
public HomeManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.HOME, session.getUriBuilder().path(PATH));
|
||||
}
|
||||
|
||||
public List<HomeNode> getHomeNodes() throws FreeboxException {
|
||||
return get(HomeNodesResponse.class, NODES_PATH);
|
||||
}
|
||||
|
||||
public HomeNode getHomeNode(int nodeId) throws FreeboxException {
|
||||
return getSingle(HomeNodesResponse.class, NODES_PATH, Integer.toString(nodeId));
|
||||
}
|
||||
|
||||
public <T> @Nullable EndpointState getEndpointsState(int nodeId, int stateSignalId) throws FreeboxException {
|
||||
return getSingle(EndpointStateResponse.class, ENDPOINTS_PATH, String.valueOf(nodeId),
|
||||
String.valueOf(stateSignalId));
|
||||
}
|
||||
|
||||
public <T> boolean putCommand(int nodeId, int stateSignalId, T value) throws FreeboxException {
|
||||
put(new EndpointValue<T>(value), ENDPOINTS_PATH, String.valueOf(nodeId), String.valueOf(stateSignalId));
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,228 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.APManager.LanAccessPoint;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.InterfacesResponse;
|
||||
|
||||
import inet.ipaddr.IPAddress;
|
||||
import inet.ipaddr.IPAddressString;
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link LanBrowserManager} is the Java class used to handle api requests related to lan
|
||||
*
|
||||
* https://dev.freebox.fr/sdk/os/system/#
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LanBrowserManager extends ListableRest<LanBrowserManager.Interface, InterfacesResponse> {
|
||||
private static final IPAddress NULL_IP = new IPAddressString("0.0.0.0").getAddress();
|
||||
private static final String PATH = "browser";
|
||||
private static final String INTERFACES = "interfaces";
|
||||
private static final String WOL_ACTION = "wol";
|
||||
|
||||
protected static class HostsResponse extends Response<LanHost> {
|
||||
}
|
||||
|
||||
protected static class InterfacesResponse extends Response<Interface> {
|
||||
}
|
||||
|
||||
public static enum Source {
|
||||
DHCP,
|
||||
NETBIOS,
|
||||
MDNS,
|
||||
MDNS_SRV,
|
||||
UPNP,
|
||||
WSD,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public record HostName(@Nullable String name, Source source) {
|
||||
}
|
||||
|
||||
protected static record Interface(String name, int hostCount) {
|
||||
}
|
||||
|
||||
private static record WakeOnLineData(String mac, String password) {
|
||||
}
|
||||
|
||||
private static enum Type {
|
||||
MAC_ADDRESS,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private static record L2Ident(MACAddress id, Type type) {
|
||||
}
|
||||
|
||||
private static record L3Connectivity(String addr, Af af, boolean active, boolean reachable,
|
||||
ZonedDateTime lastActivity, ZonedDateTime lastTimeReachable, String model) {
|
||||
|
||||
private static enum Af {
|
||||
IPV4,
|
||||
IPV6,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public IPAddress getIPAddress() {
|
||||
if (af != Af.UNKNOWN) {
|
||||
return new IPAddressString(addr).getAddress();
|
||||
}
|
||||
return NULL_IP;
|
||||
}
|
||||
}
|
||||
|
||||
public static record HostIntf(LanHost host, Interface intf) {
|
||||
}
|
||||
|
||||
private static enum HostType {
|
||||
WORKSTATION,
|
||||
LAPTOP,
|
||||
SMARTPHONE,
|
||||
TABLET,
|
||||
PRINTER,
|
||||
VG_CONSOLE,
|
||||
TELEVISION,
|
||||
NAS,
|
||||
IP_CAMERA,
|
||||
IP_PHONE,
|
||||
FREEBOX_PLAYER,
|
||||
FREEBOX_HD,
|
||||
FREEBOX_CRYSTAL,
|
||||
FREEBOX_MINI,
|
||||
FREEBOX_DELTA,
|
||||
FREEBOX_ONE,
|
||||
FREEBOX_WIFI,
|
||||
FREEBOX_POP,
|
||||
NETWORKING_DEVICE,
|
||||
MULTIMEDIA_DEVICE,
|
||||
CAR,
|
||||
OTHER,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record LanHost(String id, @Nullable String primaryName, HostType hostType, boolean primaryNameManual,
|
||||
L2Ident l2ident, @Nullable String vendorName, boolean persistent, boolean reachable,
|
||||
@Nullable ZonedDateTime lastTimeReachable, boolean active, @Nullable ZonedDateTime lastActivity,
|
||||
@Nullable ZonedDateTime firstActivity, List<HostName> names, List<L3Connectivity> l3connectivities,
|
||||
@Nullable LanAccessPoint accessPoint) {
|
||||
|
||||
public @Nullable LanAccessPoint accessPoint() {
|
||||
return accessPoint;
|
||||
}
|
||||
|
||||
public String vendorName() {
|
||||
String localVendor = vendorName;
|
||||
return localVendor != null ? localVendor : "Unknown";
|
||||
}
|
||||
|
||||
public Optional<String> getPrimaryName() {
|
||||
return Optional.ofNullable(primaryName);
|
||||
}
|
||||
|
||||
public Optional<String> getUPnPName() {
|
||||
return names.stream().filter(name -> name.source == Source.UPNP).findFirst().map(name -> name.name);
|
||||
}
|
||||
|
||||
public MACAddress getMac() {
|
||||
if (Type.MAC_ADDRESS.equals(l2ident.type)) {
|
||||
return l2ident.id;
|
||||
}
|
||||
throw new IllegalArgumentException("This host does not seem to have a Mac Address. Weird.");
|
||||
}
|
||||
|
||||
public @Nullable IPAddress getIpv4() {
|
||||
return l3connectivities.stream().filter(L3Connectivity::reachable).map(L3Connectivity::getIPAddress)
|
||||
.filter(ip -> !ip.equals(NULL_IP) && ip.isIPv4()).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public @Nullable ZonedDateTime getLastSeen() {
|
||||
ZonedDateTime localLastActivity = lastActivity;
|
||||
if (lastTimeReachable == null && localLastActivity == null) {
|
||||
return null;
|
||||
}
|
||||
if (lastTimeReachable == null) {
|
||||
return lastActivity;
|
||||
}
|
||||
if (localLastActivity == null) {
|
||||
return lastTimeReachable;
|
||||
} else {
|
||||
return localLastActivity.isAfter(lastTimeReachable) ? lastActivity : lastTimeReachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final List<Interface> interfaces = new ArrayList<>();
|
||||
|
||||
public LanBrowserManager(FreeboxOsSession session, UriBuilder uriBuilder) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, InterfacesResponse.class, uriBuilder.path(PATH));
|
||||
listSubPath = INTERFACES;
|
||||
}
|
||||
|
||||
private List<LanHost> getInterfaceHosts(String lanInterface) throws FreeboxException {
|
||||
return get(HostsResponse.class, lanInterface);
|
||||
}
|
||||
|
||||
private @Nullable LanHost getHost(String lanInterface, String hostId) throws FreeboxException {
|
||||
return getSingle(HostsResponse.class, lanInterface, hostId);
|
||||
}
|
||||
|
||||
// As the list of interfaces on the box may not change, we cache the result
|
||||
private List<Interface> getInterfaces() throws FreeboxException {
|
||||
if (interfaces.isEmpty()) {
|
||||
interfaces.addAll(getDevices());
|
||||
}
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
public synchronized List<LanHost> getHosts() throws FreeboxException {
|
||||
List<LanHost> hosts = new ArrayList<>();
|
||||
|
||||
for (Interface intf : getInterfaces()) {
|
||||
hosts.addAll(getInterfaceHosts(intf.name()));
|
||||
}
|
||||
return hosts;
|
||||
}
|
||||
|
||||
public Optional<HostIntf> getHost(MACAddress searched) throws FreeboxException {
|
||||
for (Interface intf : getInterfaces()) {
|
||||
LanHost host = getHost(intf.name(), "ether-" + searched.toColonDelimitedString());
|
||||
if (host != null) {
|
||||
return Optional.of(new HostIntf(host, intf));
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public boolean wakeOnLan(MACAddress mac, String password) throws FreeboxException {
|
||||
Optional<HostIntf> target = getHost(mac);
|
||||
if (target.isPresent()) {
|
||||
post(new WakeOnLineData(mac.toColonDelimitedString(), password), GenericResponse.class, WOL_ACTION,
|
||||
target.get().intf.name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
import inet.ipaddr.IPAddress;
|
||||
|
||||
/**
|
||||
* The {@link LanManager} is the Java class used to handle api requests related to lan
|
||||
* https://dev.freebox.fr/sdk/os/system/#
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LanManager extends ConfigurableRest<LanManager.LanConfig, LanManager.Config> {
|
||||
private static final String PATH = "lan";
|
||||
|
||||
protected static class Config extends Response<LanConfig> {
|
||||
}
|
||||
|
||||
private static enum Mode {
|
||||
ROUTER,
|
||||
BRIDGE,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record LanConfig(IPAddress ip, String name, String nameDns, String nameMdns, String nameNetbios,
|
||||
Mode mode) {
|
||||
}
|
||||
|
||||
public LanManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, Config.class, session.getUriBuilder().path(PATH), CONFIG_PATH);
|
||||
session.addManager(LanBrowserManager.class, new LanBrowserManager(session, getUriBuilder()));
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* The {@link LcdManager} is the Java class used to handle api requests related to lcd screen of the server
|
||||
* https://dev.freebox.fr/sdk/os/system/#
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LcdManager extends ConfigurableRest<LcdManager.Config, LcdManager.ConfigResponse> {
|
||||
private static final String PATH = "lcd";
|
||||
|
||||
protected static class ConfigResponse extends Response<Config> {
|
||||
}
|
||||
|
||||
public static record Config(int brightness, int orientation, boolean orientationForced) {
|
||||
}
|
||||
|
||||
public LcdManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, ConfigResponse.class, session.getUriBuilder().path(PATH),
|
||||
CONFIG_PATH);
|
||||
}
|
||||
|
||||
private void setBrightness(int brightness) throws FreeboxException {
|
||||
Config oldConfig = getConfig();
|
||||
setConfig(new Config(brightness, oldConfig.orientation, oldConfig.orientationForced));
|
||||
}
|
||||
|
||||
public void setOrientation(int orientation) throws FreeboxException {
|
||||
Config oldConfig = getConfig();
|
||||
setConfig(new Config(oldConfig.brightness, orientation, oldConfig.orientationForced));
|
||||
}
|
||||
|
||||
public void setOrientationForced(boolean forced) throws FreeboxException {
|
||||
Config oldConfig = getConfig();
|
||||
setConfig(new Config(oldConfig.brightness, oldConfig.orientation, forced));
|
||||
}
|
||||
|
||||
public void setBrightness(Callable<Integer> function) throws FreeboxException {
|
||||
try {
|
||||
setBrightness(function.call());
|
||||
} catch (Exception e) {
|
||||
throw new FreeboxException(e, "Error setting brightness");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* The {@link ListableRest} is the Java class used to handle rest answers holding a list of known equipments
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ListableRest<T, Z extends Response<T>> extends RestManager {
|
||||
private final Class<Z> deviceResponseClass;
|
||||
|
||||
protected @Nullable String listSubPath = null;
|
||||
|
||||
public ListableRest(FreeboxOsSession session, LoginManager.Permission required, Class<Z> respClass, UriBuilder uri)
|
||||
throws FreeboxException {
|
||||
super(session, required, uri);
|
||||
this.deviceResponseClass = respClass;
|
||||
}
|
||||
|
||||
public List<T> getDevices() throws FreeboxException {
|
||||
return listSubPath == null ? get(deviceResponseClass) : get(deviceResponseClass, listSubPath);
|
||||
}
|
||||
|
||||
public T getDevice(int deviceId) throws FreeboxException {
|
||||
return getSingle(deviceResponseClass, Integer.toString(deviceId));
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import static javax.xml.bind.DatatypeConverter.printHexBinary;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
|
||||
/**
|
||||
* The {@link LoginManager} is the Java class used to handle api requests related to session handling and login
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LoginManager extends RestManager {
|
||||
private static final Bundle BUNDLE = FrameworkUtil.getBundle(LoginManager.class);
|
||||
private static final String APP_ID = BUNDLE.getSymbolicName();
|
||||
private static final String ALGORITHM = "HmacSHA1";
|
||||
private static final String PATH = "login";
|
||||
private static final String SESSION = "session";
|
||||
private static final String AUTHORIZE_ACTION = "authorize";
|
||||
private static final String LOGOUT = "logout";
|
||||
|
||||
private static enum Status {
|
||||
PENDING, // the user has not confirmed the autorization request yet
|
||||
TIMEOUT, // the user did not confirmed the authorization within the given time
|
||||
GRANTED, // the app_token is valid and can be used to open a session
|
||||
DENIED, // the user denied the authorization request
|
||||
UNKNOWN; // the app_token is invalid or has been revoked
|
||||
}
|
||||
|
||||
private static record AuthorizationStatus(Status status, boolean loggedIn, String challenge,
|
||||
@Nullable String passwordSalt, boolean passwordSet) {
|
||||
}
|
||||
|
||||
private static class AuthStatus extends Response<AuthorizationStatus> {
|
||||
}
|
||||
|
||||
private static record Authorization(String appToken, String trackId) {
|
||||
}
|
||||
|
||||
private static class AuthResponse extends Response<Authorization> {
|
||||
}
|
||||
|
||||
public static enum Permission {
|
||||
PARENTAL,
|
||||
CONTACTS,
|
||||
EXPLORER,
|
||||
TV,
|
||||
WDO,
|
||||
DOWNLOADER,
|
||||
PROFILE,
|
||||
CAMERA,
|
||||
SETTINGS,
|
||||
CALLS,
|
||||
HOME,
|
||||
PVR,
|
||||
VM,
|
||||
PLAYER,
|
||||
NONE,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record Session(Map<LoginManager.Permission, @Nullable Boolean> permissions,
|
||||
@Nullable String sessionToken) {
|
||||
protected boolean hasPermission(LoginManager.Permission checked) {
|
||||
return Boolean.TRUE.equals(permissions.get(checked));
|
||||
}
|
||||
}
|
||||
|
||||
private static class SessionResponse extends Response<Session> {
|
||||
}
|
||||
|
||||
private static record AuthorizeData(String appId, String appName, String appVersion, String deviceName) {
|
||||
AuthorizeData(String appId, Bundle bundle) {
|
||||
this(appId, bundle.getHeaders().get("Bundle-Name"), bundle.getVersion().toString(),
|
||||
bundle.getHeaders().get("Bundle-Vendor"));
|
||||
}
|
||||
}
|
||||
|
||||
private static record OpenSessionData(String appId, String password) {
|
||||
}
|
||||
|
||||
private final Mac mac;
|
||||
private Optional<Authorization> authorize = Optional.empty();
|
||||
|
||||
public LoginManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, session.getUriBuilder().path(PATH));
|
||||
try {
|
||||
this.mac = Mac.getInstance(ALGORITHM);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Session openSession(String appToken) throws FreeboxException {
|
||||
AuthorizationStatus authorization = getSingle(AuthStatus.class);
|
||||
|
||||
try {
|
||||
// Initialize mac with the signing key
|
||||
mac.init(new SecretKeySpec(appToken.getBytes(), mac.getAlgorithm()));
|
||||
// Compute the hmac on input data bytes
|
||||
byte[] rawHmac = mac.doFinal(authorization.challenge().getBytes());
|
||||
// Convert raw bytes to Hex
|
||||
String password = printHexBinary(rawHmac).toLowerCase();
|
||||
return post(new OpenSessionData(APP_ID, password), SessionResponse.class, SESSION);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void closeSession() throws FreeboxException {
|
||||
post(LOGOUT);
|
||||
}
|
||||
|
||||
public String checkGrantStatus() throws FreeboxException {
|
||||
if (authorize.isEmpty()) {
|
||||
authorize = Optional.of(post(new AuthorizeData(APP_ID, BUNDLE), AuthResponse.class, AUTHORIZE_ACTION));
|
||||
}
|
||||
|
||||
return switch (getSingle(AuthStatus.class, AUTHORIZE_ACTION, authorize.get().trackId).status()) {
|
||||
case PENDING -> "";
|
||||
case GRANTED -> {
|
||||
String appToken = authorize.get().appToken;
|
||||
authorize = Optional.empty();
|
||||
yield appToken;
|
||||
}
|
||||
case TIMEOUT -> throw new FreeboxException("Unable to grant session, delay expired");
|
||||
case DENIED -> throw new FreeboxException("Unable to grant session, access was denied");
|
||||
case UNKNOWN -> throw new FreeboxException("Unable to grant session");
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.MediaReceiverManager.Receiver;
|
||||
|
||||
/**
|
||||
* The {@link MediaReceiverManager} is the Java class used to handle api requests related to air media receivers
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MediaReceiverManager extends ListableRest<Receiver, MediaReceiverManager.ReceiverResponse> {
|
||||
private static final String SUB_PATH = "receivers";
|
||||
|
||||
public static record Receiver(boolean passwordProtected, //
|
||||
Map<MediaType, Boolean> capabilities, //
|
||||
String name // This name is the UPnP name of the host
|
||||
) {
|
||||
}
|
||||
|
||||
protected static class ReceiverResponse extends Response<Receiver> {
|
||||
}
|
||||
|
||||
public static enum Action {
|
||||
START,
|
||||
STOP,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static enum MediaType {
|
||||
VIDEO,
|
||||
PHOTO,
|
||||
AUDIO,
|
||||
SCREEN,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private static record Request(String password, Action action, MediaType mediaType, @Nullable String media,
|
||||
int position) {
|
||||
}
|
||||
|
||||
public MediaReceiverManager(FreeboxOsSession session, UriBuilder uriBuilder) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, ReceiverResponse.class, uriBuilder.path(SUB_PATH));
|
||||
}
|
||||
|
||||
public @Nullable Receiver getReceiver(String receiverName) throws FreeboxException {
|
||||
return getDevices().stream().filter(rcv -> receiverName.equals(rcv.name())).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public void sendToReceiver(String receiver, String password, Action action, MediaType type)
|
||||
throws FreeboxException {
|
||||
sendToReceiver(receiver, new Request(password, action, type, null, 0));
|
||||
}
|
||||
|
||||
public void sendToReceiver(String receiver, String password, Action action, MediaType type, String url)
|
||||
throws FreeboxException {
|
||||
sendToReceiver(receiver, new Request(password, action, type, url, 0));
|
||||
}
|
||||
|
||||
private void sendToReceiver(String receiver, Request payload) throws FreeboxException {
|
||||
post(payload, GenericResponse.class, receiver);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
|
||||
/**
|
||||
* The {@link NetShareManager} is the Java class used to handle api requests related to network shares
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NetShareManager extends RestManager {
|
||||
private static final String PATH = "netshare";
|
||||
|
||||
public NetShareManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, session.getUriBuilder().path(PATH));
|
||||
session.addManager(SambaManager.class, new SambaManager(session, getUriBuilder()));
|
||||
session.addManager(AfpManager.class, new AfpManager(session, getUriBuilder()));
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* The {@link PhoneManager} is the Java class used to handle api requests related to phone and calls
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PhoneManager extends ConfigurableRest<PhoneManager.Config, PhoneManager.ConfigResponse> {
|
||||
private static final String DECT_PAGE_ACTION = "dect_page_%s";
|
||||
private static final String FXS_RING_ACTION = "fxs_ring_%s";
|
||||
private static final String PATH = "phone";
|
||||
|
||||
protected class ConfigResponse extends Response<Config> {
|
||||
}
|
||||
|
||||
protected class StatusResponse extends Response<Status> {
|
||||
}
|
||||
|
||||
private static enum NetworkStatus {
|
||||
WORKING,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record Config(NetworkStatus network, boolean dectEcoMode, String dectPin, int dectRingPattern,
|
||||
boolean dectRegistration, boolean dectNemoMode, boolean dectEnabled, boolean dectRingOnOff) {
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
FXS,
|
||||
DECT,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record Status(int id, boolean isRinging, boolean onHook, boolean hardwareDefect, Type type,
|
||||
@Nullable String vendor, int gainRx, int gainTx) {
|
||||
|
||||
public String vendor() {
|
||||
String localVendor = vendor;
|
||||
return localVendor != null ? localVendor : "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public PhoneManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.CALLS, ConfigResponse.class, session.getUriBuilder().path(PATH),
|
||||
CONFIG_PATH);
|
||||
}
|
||||
|
||||
public List<Status> getPhoneStatuses() throws FreeboxException {
|
||||
return get(StatusResponse.class, "");
|
||||
}
|
||||
|
||||
public Optional<Status> getStatus(int id) throws FreeboxException {
|
||||
return Optional.ofNullable(getSingle(StatusResponse.class, Integer.toString(id)));
|
||||
}
|
||||
|
||||
public void ringFxs(boolean startIt) throws FreeboxException {
|
||||
post(FXS_RING_ACTION.formatted(startIt ? "start" : "stop"));
|
||||
}
|
||||
|
||||
public void ringDect(boolean startIt) throws FreeboxException {
|
||||
post(DECT_PAGE_ACTION.formatted(startIt ? "start" : "stop"));
|
||||
}
|
||||
|
||||
public void setGainRx(int clientId, int gain) throws FreeboxException {
|
||||
Optional<Status> result = getStatus(clientId);
|
||||
if (result.isPresent()) {
|
||||
Status status = result.get();
|
||||
Status newStatus = new Status(status.id, status.isRinging, status.onHook, status.hardwareDefect,
|
||||
status.type, status.vendor, gain, status.gainTx);
|
||||
put(StatusResponse.class, newStatus, Integer.toString(clientId));
|
||||
}
|
||||
}
|
||||
|
||||
public void setGainTx(int clientId, int gain) throws FreeboxException {
|
||||
Optional<Status> result = getStatus(clientId);
|
||||
if (result.isPresent()) {
|
||||
Status status = result.get();
|
||||
Status newStatus = new Status(status.id, status.isRinging, status.onHook, status.hardwareDefect,
|
||||
status.type, status.vendor, status.gainRx, gain);
|
||||
put(StatusResponse.class, newStatus, Integer.toString(clientId));
|
||||
}
|
||||
}
|
||||
|
||||
public void alternateRing(boolean status) throws FreeboxException {
|
||||
Config config = getConfig();
|
||||
Config newConfig = new Config(config.network, config.dectEcoMode, config.dectPin, config.dectRingPattern,
|
||||
config.dectRegistration, config.dectNemoMode, config.dectEnabled, status);
|
||||
put(ConfigResponse.class, newConfig, CONFIG_PATH);
|
||||
}
|
||||
|
||||
public boolean setStatus(boolean enabled) throws FreeboxException {
|
||||
Config config = getConfig();
|
||||
Config newConfig = new Config(config.network, config.dectEcoMode, config.dectPin, config.dectRingPattern,
|
||||
config.dectRegistration, config.dectNemoMode, enabled, config.dectRingOnOff);
|
||||
return setConfig(newConfig).dectEnabled;
|
||||
}
|
||||
}
|
@ -0,0 +1,246 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.THING_PLAYER;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Metadata.PlaybackState;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Metadata.SubtitleTrack;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Metadata.VideoTrack;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.PlayerContext.PlayerDetails;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.SystemManager.ModelInfo;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link PlayerManager} is the Java class used to handle api requests related to player
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlayerManager extends ListableRest<PlayerManager.Player, PlayerManager.PlayerResponse> {
|
||||
private static final String STATUS_PATH = "status";
|
||||
|
||||
protected static class PlayerResponse extends Response<Player> {
|
||||
}
|
||||
|
||||
public static enum DeviceModel {
|
||||
FBX7HD_DELTA, // Freebox Player Devialet
|
||||
TBX8AM, // Player Pop
|
||||
FBX6HD,
|
||||
FBX6LC,
|
||||
FBX6LCV2,
|
||||
FBX7HD,
|
||||
FBX7HD_ONE,
|
||||
FBX8AM,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record Player(MACAddress mac, StbType stbType, int id, ZonedDateTime lastTimeReachable,
|
||||
boolean apiAvailable, String deviceName, DeviceModel deviceModel, boolean reachable, String uid,
|
||||
@Nullable String apiVersion, List<String> lanGids) {
|
||||
private static enum StbType {
|
||||
STB_ANDROID,
|
||||
STB_V6,
|
||||
STB_V7,
|
||||
STB_V8,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a string like eg: '17/api/v8'
|
||||
*/
|
||||
private @Nullable String baseUrl() {
|
||||
String api = apiVersion;
|
||||
return api != null ? "%d/api/v%s/".formatted(id, api.split("\\.")[0]) : null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class StatusResponse extends Response<Status> {
|
||||
}
|
||||
|
||||
public static enum PowerState {
|
||||
STANDBY,
|
||||
RUNNING,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record Status(PowerState powerState, StatusInformation player,
|
||||
@Nullable ForegroundApp foregroundApp) {
|
||||
|
||||
public @Nullable ForegroundApp foregroundApp() {
|
||||
return foregroundApp;
|
||||
}
|
||||
}
|
||||
|
||||
public static record ForegroundApp(int packageId, @Nullable String curlUrl, @Nullable Object context,
|
||||
@SerializedName(value = "package") String _package) {
|
||||
}
|
||||
|
||||
private static record StatusInformation(String name, ZonedDateTime lastActivity) {
|
||||
}
|
||||
|
||||
private static class ConfigurationResponse extends Response<Configuration> {
|
||||
}
|
||||
|
||||
public static record Configuration(String boardName, boolean configured, String firmwareVersion,
|
||||
@Nullable ModelInfo modelInfo, String serial, String uptime, long uptimeVal) {
|
||||
}
|
||||
|
||||
private enum MediaState {
|
||||
READY,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private static record AudioTrack(int bitrate, @SerializedName("channelCount") int channelCount,
|
||||
@Nullable String codec, @SerializedName("codecId") @Nullable String codecId, @Nullable String language,
|
||||
@SerializedName("metadataId") @Nullable String metadataId, int pid, int samplerate, long uid) {
|
||||
}
|
||||
|
||||
private static enum Type {
|
||||
NORMAL,
|
||||
HEARINGIMPAIRED,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
protected static record Metadata(@Nullable String album,
|
||||
@SerializedName("albumArtist") @Nullable String albumArtist, @Nullable String artist,
|
||||
@Nullable String author, int bpm, @Nullable String comment, boolean compilation, @Nullable String composer,
|
||||
@Nullable String container, @Nullable String copyright, long date,
|
||||
@SerializedName("discId") @Nullable String discId, @SerializedName("discNumber") int discNumber,
|
||||
@SerializedName("discTotal") int discTotal, @Nullable String genre,
|
||||
@SerializedName("musicbrainzDiscId") @Nullable String musicbrainzDiscId, @Nullable String performer,
|
||||
@Nullable String title, @SerializedName("trackNumber") int trackNumber,
|
||||
@SerializedName("trackTotal") int trackTotal, @Nullable String url) {
|
||||
|
||||
protected static enum PlaybackState {
|
||||
PLAY,
|
||||
PAUSE,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
protected static record SubtitleTrack(@Nullable String codec, @Nullable String language, @Nullable String pid,
|
||||
Type type, @Nullable String uid) {
|
||||
}
|
||||
|
||||
protected static record VideoTrack(int bitrate, @Nullable String codec, int height, int pid, int uid,
|
||||
int width) {
|
||||
}
|
||||
}
|
||||
|
||||
public static record PlayerContext(@Nullable PlayerDetails player) {
|
||||
public static record PlayerDetails(@SerializedName("audioIndex") int audioIndex,
|
||||
@SerializedName("audioList") List<AudioTrack> audioList, @SerializedName("curPos") long curPos,
|
||||
int duration, @SerializedName("livePos") long livePos, @SerializedName("maxPos") long maxPos,
|
||||
@SerializedName("mediaState") MediaState mediaState, @Nullable Metadata metadata,
|
||||
@SerializedName("minPos") long minPos, @SerializedName("playbackState") PlaybackState playbackState,
|
||||
long position, @Nullable String source, @SerializedName("subtitleIndex") int subtitleIndex,
|
||||
@SerializedName("subtitleList") List<SubtitleTrack> subtitleList,
|
||||
@SerializedName("videoIndex") int videoIndex, @SerializedName("videoList") List<VideoTrack> videoList) {
|
||||
}
|
||||
}
|
||||
|
||||
private static enum BouquetType {
|
||||
ADSL,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private static enum ChannelType {
|
||||
REGULAR,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private static record Service(long id, @Nullable String name,
|
||||
@SerializedName("qualityLabel") @Nullable String qualityLabel,
|
||||
@SerializedName("qualityName") @Nullable String qualityName, @SerializedName("sortInfo") int sortInfo,
|
||||
@SerializedName("typeLabel") @Nullable String typeLabel,
|
||||
@SerializedName("typeName") @Nullable String typeName, @Nullable String url) {
|
||||
}
|
||||
|
||||
private static record Channel(@SerializedName("bouquetId") long bouquetId,
|
||||
@SerializedName("bouquetName") @Nullable String bouquetName,
|
||||
@SerializedName("bouquetType") BouquetType bouquetType,
|
||||
@SerializedName("channelName") @Nullable String channelName,
|
||||
@SerializedName("channelNumber") int channelNumber,
|
||||
@SerializedName("channelSubNumber") int channelSubNumber,
|
||||
@SerializedName("channelType") ChannelType channelType,
|
||||
@SerializedName("channelUuid") @Nullable String channelUuid,
|
||||
@SerializedName("currentServiceIndex") int currentServiceIndex,
|
||||
@SerializedName("isTimeShifting") boolean isTimeShifting, List<Service> services,
|
||||
@SerializedName("videoIsVisible") boolean videoIsVisible) {
|
||||
}
|
||||
|
||||
public static record TvContext(@Nullable Channel channel, @Nullable PlayerDetails player) {
|
||||
}
|
||||
|
||||
private final Map<Integer, String> subPaths = new HashMap<>();
|
||||
|
||||
public PlayerManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.PLAYER, PlayerResponse.class,
|
||||
session.getUriBuilder().path(THING_PLAYER));
|
||||
getDevices().stream().filter(Player::apiAvailable).forEach(player -> {
|
||||
String baseUrl = player.baseUrl();
|
||||
if (baseUrl != null) {
|
||||
subPaths.put(player.id, baseUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Status getPlayerStatus(int id) throws FreeboxException {
|
||||
return getSingle(StatusResponse.class, subPaths.get(id), STATUS_PATH);
|
||||
}
|
||||
|
||||
// The player API does not allow to directly request a given player like others api parts
|
||||
@Override
|
||||
public Player getDevice(int id) throws FreeboxException {
|
||||
return getDevices().stream().filter(player -> player.id == id).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public Configuration getConfig(int id) throws FreeboxException {
|
||||
return getSingle(ConfigurationResponse.class, subPaths.get(id), SYSTEM_PATH);
|
||||
}
|
||||
|
||||
public void sendKey(String ip, String code, String key, boolean longPress, int count) {
|
||||
UriBuilder uriBuilder = UriBuilder.fromPath("pub").scheme("http").host(ip).path("remote_control");
|
||||
uriBuilder.queryParam("code", code).queryParam("key", key);
|
||||
if (longPress) {
|
||||
uriBuilder.queryParam("long", true);
|
||||
}
|
||||
if (count > 1) {
|
||||
uriBuilder.queryParam("repeat", count);
|
||||
}
|
||||
try {
|
||||
session.execute(uriBuilder.build(), HttpMethod.GET, GenericResponse.class, null);
|
||||
} catch (FreeboxException ignore) {
|
||||
// This call does not return anything, we can safely ignore
|
||||
}
|
||||
}
|
||||
|
||||
public void reboot(int id) throws FreeboxException {
|
||||
post(subPaths.get(id), SYSTEM_PATH, REBOOT_ACTION);
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.HostsResponse;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
|
||||
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link RepeaterManager} is the Java class used to handle api requests related to repeater
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RepeaterManager extends ListableRest<RepeaterManager.Repeater, RepeaterManager.RepeaterResponse> {
|
||||
|
||||
protected static class RepeaterResponse extends Response<Repeater> {
|
||||
}
|
||||
|
||||
protected static class RepeaterLedResponse extends Response<RepeaterLed> {
|
||||
}
|
||||
|
||||
public static record RepeaterLed(int id, boolean ledActivated) {
|
||||
}
|
||||
|
||||
private static enum Connection {
|
||||
CONNECTED,
|
||||
DISCONNECTED,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private static enum Status {
|
||||
STARTING,
|
||||
RUNNING,
|
||||
REBOOTING,
|
||||
UPDATING,
|
||||
REBOOT_FAILURE,
|
||||
UPDATE_FAILURE,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static enum Model {
|
||||
FBXWMR, // Répéteur Wifi
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record Repeater(int id, boolean ledActivated, boolean enabled, MACAddress mainMac,
|
||||
Connection connection, ZonedDateTime bootTime, Status status, String name, String sn, String apiVer,
|
||||
ZonedDateTime lastSeen, Model model, String firmwareVersion) {
|
||||
|
||||
public long getUptimeVal() {
|
||||
return Duration.between(bootTime, ZonedDateTime.now()).toSeconds();
|
||||
}
|
||||
}
|
||||
|
||||
public RepeaterManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, RepeaterResponse.class,
|
||||
session.getUriBuilder().path(THING_REPEATER));
|
||||
}
|
||||
|
||||
public List<LanHost> getRepeaterHosts(int id) throws FreeboxException {
|
||||
return get(HostsResponse.class, Integer.toString(id), THING_HOST);
|
||||
}
|
||||
|
||||
public synchronized List<LanHost> getHosts() throws FreeboxException {
|
||||
List<LanHost> hosts = new ArrayList<>();
|
||||
for (Repeater rep : getDevices()) {
|
||||
if (Connection.CONNECTED.equals(rep.connection)) {
|
||||
hosts.addAll(getRepeaterHosts(rep.id));
|
||||
}
|
||||
}
|
||||
return hosts;
|
||||
}
|
||||
|
||||
public Optional<LanHost> getHost(MACAddress mac) throws FreeboxException {
|
||||
return getHosts().stream().filter(host -> host.getMac().equals(mac)).findFirst();
|
||||
}
|
||||
|
||||
public void reboot(int id) throws FreeboxException {
|
||||
post(Integer.toString(id), REBOOT_ACTION);
|
||||
}
|
||||
|
||||
public Optional<RepeaterLed> led(int id, boolean enable) throws FreeboxException {
|
||||
RepeaterLed result = put(RepeaterLedResponse.class, new RepeaterLed(id, enable), Integer.toString(id));
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import static org.eclipse.jetty.http.HttpMethod.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.PermissionException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* Base class for the various rest managers available through the API
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RestManager {
|
||||
protected static final String REBOOT_ACTION = "reboot";
|
||||
protected static final String SYSTEM_PATH = "system";
|
||||
|
||||
protected class GenericResponse extends Response<Object> {
|
||||
}
|
||||
|
||||
private final UriBuilder uriBuilder;
|
||||
protected final FreeboxOsSession session;
|
||||
|
||||
public RestManager(FreeboxOsSession session, LoginManager.Permission required, UriBuilder uri)
|
||||
throws FreeboxException {
|
||||
this.uriBuilder = uri;
|
||||
this.session = session;
|
||||
if (required != LoginManager.Permission.NONE && !session.hasPermission(required)) {
|
||||
throw new PermissionException(required, "Permission missing: %s", required.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected UriBuilder getUriBuilder() {
|
||||
return uriBuilder.clone();
|
||||
}
|
||||
|
||||
private URI buildUri(String... pathElements) {
|
||||
UriBuilder localBuilder = getUriBuilder();
|
||||
for (String path : pathElements) {
|
||||
localBuilder.path(path);
|
||||
}
|
||||
return localBuilder.build();
|
||||
}
|
||||
|
||||
// Returns the first and supposed only element from the list. Presence of this element is expected and mandatory
|
||||
private <F> F controlSingleton(List<F> result) {
|
||||
if (result.size() == 1) {
|
||||
return result.get(0);
|
||||
}
|
||||
throw new IllegalArgumentException("Result is empty or not singleton");
|
||||
}
|
||||
|
||||
protected <F, T extends Response<F>> List<F> get(Class<T> clazz, String... pathElements) throws FreeboxException {
|
||||
return session.execute(buildUri(pathElements), GET, clazz, null);
|
||||
}
|
||||
|
||||
protected <F, T extends Response<F>> F getSingle(Class<T> clazz, String... pathElements) throws FreeboxException {
|
||||
return controlSingleton(get(clazz, pathElements));
|
||||
}
|
||||
|
||||
protected <F, T extends Response<F>> F post(Object payload, Class<T> clazz, String... pathElements)
|
||||
throws FreeboxException {
|
||||
return controlSingleton(session.execute(buildUri(pathElements), POST, clazz, payload));
|
||||
}
|
||||
|
||||
protected void post(String... pathElements) throws FreeboxException {
|
||||
session.execute(buildUri(pathElements), POST, GenericResponse.class, null);
|
||||
}
|
||||
|
||||
protected <F, T extends Response<F>> F put(Class<T> clazz, F payload, String... pathElements)
|
||||
throws FreeboxException {
|
||||
return controlSingleton(session.execute(buildUri(pathElements), PUT, clazz, payload));
|
||||
}
|
||||
|
||||
protected <F, T extends Response<F>> void put(F payload, String... pathElements) throws FreeboxException {
|
||||
session.execute(buildUri(pathElements), PUT, GenericResponse.class, payload);
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* The {@link SambaManager} is the Java class used to handle api requests related to Samba shares
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SambaManager extends ConfigurableRest<SambaManager.Samba, SambaManager.ConfigResponse> {
|
||||
private static final String PATH = "samba";
|
||||
|
||||
protected static class ConfigResponse extends Response<Samba> {
|
||||
}
|
||||
|
||||
public static record Samba(boolean fileShareEnabled, boolean printShareEnabled, boolean logonEnabled,
|
||||
@Nullable String logonUser, @Nullable String logonPassword, @Nullable String workgroup,
|
||||
boolean smbv2Enabled) {
|
||||
}
|
||||
|
||||
public SambaManager(FreeboxOsSession session, UriBuilder uriBuilder) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, ConfigResponse.class, uriBuilder.path(PATH), null);
|
||||
}
|
||||
|
||||
public boolean setFileShare(boolean enable) throws FreeboxException {
|
||||
Samba config = getConfig();
|
||||
Samba newConfig = new Samba(enable, config.printShareEnabled, config.logonEnabled, config.logonUser,
|
||||
config.logonPassword, config.workgroup, config.smbv2Enabled);
|
||||
return setConfig(newConfig).fileShareEnabled();
|
||||
}
|
||||
|
||||
public boolean setPrintShare(boolean enable) throws FreeboxException {
|
||||
Samba config = getConfig();
|
||||
Samba newConfig = new Samba(config.fileShareEnabled, enable, config.logonEnabled, config.logonUser,
|
||||
config.logonPassword, config.workgroup, config.smbv2Enabled);
|
||||
return setConfig(newConfig).printShareEnabled();
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession.BoxModel;
|
||||
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link SystemManager} is the Java class used to handle api requests related to system
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SystemManager extends ConfigurableRest<SystemManager.Config, SystemManager.ConfigurationResponse> {
|
||||
|
||||
protected static class ConfigurationResponse extends Response<Config> {
|
||||
}
|
||||
|
||||
public static record Sensor(String id, String name, int value) {
|
||||
public enum SensorKind {
|
||||
FAN,
|
||||
TEMP,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public SensorKind getKind() {
|
||||
String[] elements = id.split("_");
|
||||
if (elements.length > 0) {
|
||||
String kind = elements[0].replaceAll("\\d", "").toUpperCase();
|
||||
try {
|
||||
return SensorKind.valueOf(kind);
|
||||
} catch (IllegalArgumentException ignore) { // returning UNKNOWN
|
||||
}
|
||||
}
|
||||
return SensorKind.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
private static record Expansion(int slot, boolean probeDone, boolean present, boolean supported, String bundle,
|
||||
Type type) {
|
||||
private static enum Type {
|
||||
UNKNOWN, // unknown module
|
||||
DSL_LTE, // xDSL + LTE
|
||||
DSL_LTE_EXTERNAL_ANTENNAS, // xDSL + LTE with external antennas switch
|
||||
FTTH_P2P, // FTTH P2P
|
||||
FTTH_PON, // FTTH PON
|
||||
SECURITY; // Security module
|
||||
}
|
||||
}
|
||||
|
||||
public static record ModelInfo(BoxModel name, String prettyName, boolean hasExpansions, boolean hasLanSfp,
|
||||
boolean hasDect, boolean hasHomeAutomation, boolean hasFemtocellExp, boolean hasFixedFemtocell,
|
||||
boolean hasVm) {
|
||||
}
|
||||
|
||||
public static record Config(String firmwareVersion, MACAddress mac, String serial, String uptime, long uptimeVal,
|
||||
String boardName, boolean boxAuthenticated, DiskStatus diskStatus, String userMainStorage,
|
||||
List<Sensor> sensors, ModelInfo modelInfo, List<Sensor> fans, List<Expansion> expansions) {
|
||||
private static enum DiskStatus {
|
||||
NOT_DETECTED,
|
||||
DISABLED,
|
||||
INITIALIZING,
|
||||
ERROR,
|
||||
ACTIVE,
|
||||
UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public SystemManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, ConfigurationResponse.class,
|
||||
session.getUriBuilder().path(SYSTEM_PATH), null);
|
||||
}
|
||||
|
||||
public void reboot() throws FreeboxException {
|
||||
post(REBOOT_ACTION);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* The {@link UPnPAVManager} is the Java class used to handle api requests related to UPnP AV
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class UPnPAVManager extends ConfigurableRest<UPnPAVManager.Config, UPnPAVManager.ConfigResponse> {
|
||||
private static final String PATH = "upnpav";
|
||||
|
||||
protected static class ConfigResponse extends Response<Config> {
|
||||
}
|
||||
|
||||
protected static record Config(boolean enabled) {
|
||||
}
|
||||
|
||||
public UPnPAVManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, ConfigResponse.class, session.getUriBuilder().path(PATH),
|
||||
CONFIG_PATH);
|
||||
}
|
||||
|
||||
public boolean getStatus() throws FreeboxException {
|
||||
return getConfig().enabled();
|
||||
}
|
||||
|
||||
public boolean setStatus(boolean enabled) throws FreeboxException {
|
||||
return setConfig(new Config(enabled)).enabled();
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.THING_VM;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link VmManager} is the Java class used to handle api requests related to virtual machines
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class VmManager extends ListableRest<VmManager.VirtualMachine, VmManager.VirtualMachineResponse> {
|
||||
|
||||
protected class VirtualMachineResponse extends Response<VirtualMachine> {
|
||||
}
|
||||
|
||||
public static enum Status {
|
||||
STOPPED,
|
||||
RUNNING,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
public static record VirtualMachine(int id, String name, MACAddress mac, Status status) {
|
||||
}
|
||||
|
||||
public VmManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.VM, VirtualMachineResponse.class,
|
||||
session.getUriBuilder().path(THING_VM));
|
||||
}
|
||||
|
||||
public void power(int vmId, boolean startIt) throws FreeboxException {
|
||||
post(Integer.toString(vmId), startIt ? "start" : "powerbutton");
|
||||
}
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketListener;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.openhab.binding.freeboxos.internal.api.ApiHandler;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.VmManager.VirtualMachine;
|
||||
import org.openhab.binding.freeboxos.internal.handler.HostHandler;
|
||||
import org.openhab.binding.freeboxos.internal.handler.VmHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link WebSocketManager} is the Java class register to the websocket server and handle notifications
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WebSocketManager extends RestManager implements WebSocketListener {
|
||||
private static final String HOST_UNREACHABLE = "lan_host_l3addr_unreachable";
|
||||
private static final String HOST_REACHABLE = "lan_host_l3addr_reachable";
|
||||
private static final String VM_CHANGED = "vm_state_changed";
|
||||
private static final Register REGISTRATION = new Register("register",
|
||||
List.of(VM_CHANGED, HOST_REACHABLE, HOST_UNREACHABLE));
|
||||
private static final String WS_PATH = "ws/event";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WebSocketManager.class);
|
||||
private final Map<MACAddress, HostHandler> lanHosts = new HashMap<>();
|
||||
private final Map<Integer, VmHandler> vms = new HashMap<>();
|
||||
private final ApiHandler apiHandler;
|
||||
|
||||
private volatile @Nullable Session wsSession;
|
||||
|
||||
private record Register(String action, List<String> events) {
|
||||
|
||||
}
|
||||
|
||||
public WebSocketManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, session.getUriBuilder().path(WS_PATH));
|
||||
this.apiHandler = session.getApiHandler();
|
||||
}
|
||||
|
||||
private static enum Action {
|
||||
REGISTER,
|
||||
NOTIFICATION,
|
||||
UNKNOWN;
|
||||
}
|
||||
|
||||
private static record WebSocketResponse(boolean success, Action action, String event, String source,
|
||||
@Nullable JsonElement result) {
|
||||
public String getEvent() {
|
||||
return source + "_" + event;
|
||||
}
|
||||
}
|
||||
|
||||
public void openSession(@Nullable String sessionToken) throws FreeboxException {
|
||||
WebSocketClient client = new WebSocketClient(apiHandler.getHttpClient());
|
||||
URI uri = getUriBuilder().scheme(getUriBuilder().build().getScheme().contains("s") ? "wss" : "ws").build();
|
||||
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
||||
request.setHeader(ApiHandler.AUTH_HEADER, sessionToken);
|
||||
|
||||
try {
|
||||
client.start();
|
||||
client.connect(this, uri, request);
|
||||
} catch (Exception e) {
|
||||
throw new FreeboxException(e, "Exception connecting websocket client");
|
||||
}
|
||||
}
|
||||
|
||||
public void closeSession() {
|
||||
logger.debug("Awaiting closure from remote");
|
||||
Session localSession = wsSession;
|
||||
if (localSession != null) {
|
||||
localSession.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketConnect(@NonNullByDefault({}) Session wsSession) {
|
||||
this.wsSession = wsSession;
|
||||
logger.debug("Websocket connection establisehd");
|
||||
try {
|
||||
wsSession.getRemote().sendString(apiHandler.serialize(REGISTRATION));
|
||||
} catch (IOException e) {
|
||||
logger.warn("Error connecting to websocket: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketText(@NonNullByDefault({}) String message) {
|
||||
Session localSession = wsSession;
|
||||
if (message.toLowerCase(Locale.US).contains("bye") && localSession != null) {
|
||||
localSession.close(StatusCode.NORMAL, "Thanks");
|
||||
return;
|
||||
}
|
||||
|
||||
WebSocketResponse result = apiHandler.deserialize(WebSocketResponse.class, message);
|
||||
if (result.success) {
|
||||
switch (result.action) {
|
||||
case REGISTER:
|
||||
logger.debug("Event registration successfull");
|
||||
break;
|
||||
case NOTIFICATION:
|
||||
handleNotification(result);
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unhandled notification received: {}", result.action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNotification(WebSocketResponse result) {
|
||||
JsonElement json = result.result;
|
||||
if (json != null) {
|
||||
switch (result.getEvent()) {
|
||||
case VM_CHANGED:
|
||||
VirtualMachine vm = apiHandler.deserialize(VirtualMachine.class, json.toString());
|
||||
logger.debug("Received notification for VM {}", vm.id());
|
||||
VmHandler vmHandler = vms.get(vm.id());
|
||||
if (vmHandler != null) {
|
||||
vmHandler.updateVmChannels(vm);
|
||||
}
|
||||
break;
|
||||
case HOST_UNREACHABLE, HOST_REACHABLE:
|
||||
LanHost host = apiHandler.deserialize(LanHost.class, json.toString());
|
||||
MACAddress mac = host.getMac();
|
||||
logger.debug("Received notification for LanHost {}", mac.toColonDelimitedString());
|
||||
HostHandler hostHandler = lanHosts.get(mac);
|
||||
if (hostHandler != null) {
|
||||
hostHandler.updateConnectivityChannels(host);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unhandled event received: {}", result.getEvent());
|
||||
}
|
||||
} else {
|
||||
logger.warn("Empty json element in notification");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketClose(int statusCode, @NonNullByDefault({}) String reason) {
|
||||
logger.debug("Socket Closed: [{}] - reason {}", statusCode, reason);
|
||||
this.wsSession = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketError(@NonNullByDefault({}) Throwable cause) {
|
||||
logger.warn("Error on websocket: {}", cause.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketBinary(byte @Nullable [] payload, int offset, int len) {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
public void registerListener(MACAddress mac, HostHandler hostHandler) {
|
||||
lanHosts.put(mac, hostHandler);
|
||||
}
|
||||
|
||||
public void unregisterListener(MACAddress mac) {
|
||||
lanHosts.remove(mac);
|
||||
}
|
||||
|
||||
public void registerVm(int clientId, VmHandler vmHandler) {
|
||||
vms.put(clientId, vmHandler);
|
||||
}
|
||||
|
||||
public void unregisterVm(int clientId) {
|
||||
vms.remove(clientId);
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.api.rest;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.Response;
|
||||
|
||||
/**
|
||||
* The {@link WifiManager} is the Java class used to handle api requests related to wifi
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WifiManager extends ConfigurableRest<WifiManager.Config, WifiManager.ConfigResponse> {
|
||||
private static final String PATH = "wifi";
|
||||
|
||||
protected static class ConfigResponse extends Response<Config> {
|
||||
}
|
||||
|
||||
protected static record Config(boolean enabled) {
|
||||
}
|
||||
|
||||
public WifiManager(FreeboxOsSession session) throws FreeboxException {
|
||||
super(session, LoginManager.Permission.NONE, ConfigResponse.class, session.getUriBuilder().path(PATH),
|
||||
CONFIG_PATH);
|
||||
session.addManager(APManager.class, new APManager(session, getUriBuilder()));
|
||||
}
|
||||
|
||||
public boolean getStatus() throws FreeboxException {
|
||||
return getConfig().enabled();
|
||||
}
|
||||
|
||||
public boolean setStatus(boolean enabled) throws FreeboxException {
|
||||
return setConfig(new Config(enabled)).enabled();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ApiConsumerConfiguration {
|
||||
public static final String REFRESH_INTERVAL = "refreshInterval";
|
||||
|
||||
public int refreshInterval = 30;
|
||||
public String password = "";
|
||||
public boolean acceptAllMp3 = true;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link ClientConfiguration} is responsible for holding configuration informations for a controllable client of
|
||||
* the API
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ClientConfiguration extends HostConfiguration {
|
||||
public static final String ID = "id";
|
||||
|
||||
public int id = 1;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.config;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxTlsCertificateProvider;
|
||||
|
||||
/**
|
||||
* The {@link FreeboxOsConfiguration} is responsible for holding configuration informations needed to access the Freebox
|
||||
* API
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FreeboxOsConfiguration {
|
||||
public static final String API_DOMAIN = "apiDomain";
|
||||
public static final String APP_TOKEN = "appToken";
|
||||
public static final String HTTPS_PORT = "httpsPort";
|
||||
public static final String HTTPS_AVAILABLE = "httpsAvailable";
|
||||
|
||||
private String apiDomain = FreeboxTlsCertificateProvider.DEFAULT_NAME;
|
||||
public String appToken = "";
|
||||
public boolean discoverNetDevice;
|
||||
|
||||
private int httpsPort = 15682;
|
||||
private boolean httpsAvailable;
|
||||
|
||||
private String getScheme() {
|
||||
return httpsAvailable ? "https" : "http";
|
||||
}
|
||||
|
||||
private int getPort() {
|
||||
return httpsAvailable ? httpsPort : 80;
|
||||
}
|
||||
|
||||
public UriBuilder getUriBuilder(String path) {
|
||||
return UriBuilder.fromPath("/").scheme(getScheme()).port(getPort()).host(apiDomain).path(path).clone();
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.config;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.FreeplugManager.Freeplug;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link FreeplugConfigurationBuilder} is responsible for holding configuration informations associated to a
|
||||
* Freeplug
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FreeplugConfigurationBuilder {
|
||||
private static final FreeplugConfigurationBuilder BUILDER_INSTANCE = new FreeplugConfigurationBuilder();
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FreeplugConfigurationBuilder.class);
|
||||
|
||||
private FreeplugConfigurationBuilder() {
|
||||
}
|
||||
|
||||
public static FreeplugConfigurationBuilder getInstance() {
|
||||
return BUILDER_INSTANCE;
|
||||
}
|
||||
|
||||
public DiscoveryResultBuilder configure(ThingUID bridgeUID, Freeplug plug) {
|
||||
MACAddress mac = plug.id();
|
||||
String uid = mac.toHexString(false);
|
||||
ThingUID thingUID = new ThingUID(THING_TYPE_FREEPLUG, bridgeUID, uid);
|
||||
|
||||
logger.debug("Adding new {} {} to inbox", THING_FREEPLUG, thingUID);
|
||||
|
||||
return DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
|
||||
.withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS)
|
||||
.withLabel("%s (%s) %s".formatted(THING_FREEPLUG, plug.netRole().name(), uid))
|
||||
.withProperty(Thing.PROPERTY_MAC_ADDRESS, mac.toColonDelimitedString());
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
import inet.ipaddr.MACAddressString;
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link HostConfiguration} is responsible for holding
|
||||
* configuration informations associated to a Freebox Network Device
|
||||
* thing type
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HostConfiguration extends ApiConsumerConfiguration {
|
||||
private String macAddress = "";
|
||||
|
||||
public MACAddress getMac() {
|
||||
return new MACAddressString(macAddress).getAddress();
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link LandlineConfiguration} is responsible for holding
|
||||
* configuration informations associated to a Freebox Phone thing type
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LandlineConfiguration extends ApiConsumerConfiguration {
|
||||
public int id = 1;
|
||||
|
||||
LandlineConfiguration() {
|
||||
refreshInterval = 2;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.config;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.Category;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.HomeNode;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
|
||||
/**
|
||||
* The {@link NodeConfigurationBuilder} is responsible for holding configuration informations associated to a Freebox
|
||||
* Home thing type
|
||||
*
|
||||
* @author ben12 - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NodeConfigurationBuilder {
|
||||
private static final NodeConfigurationBuilder BUILDER_INSTANCE = new NodeConfigurationBuilder();
|
||||
|
||||
private NodeConfigurationBuilder() {
|
||||
}
|
||||
|
||||
public static NodeConfigurationBuilder getInstance() {
|
||||
return BUILDER_INSTANCE;
|
||||
}
|
||||
|
||||
public Optional<DiscoveryResultBuilder> configure(ThingUID bridgeUID, HomeNode node) {
|
||||
if (node.category() == Category.UNKNOWN) {
|
||||
return Optional.empty();
|
||||
}
|
||||
ThingUID thingUID = new ThingUID(node.category().getThingTypeUID(), bridgeUID, Integer.toString(node.id()));
|
||||
DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(thingUID);
|
||||
discoveryResultBuilder.withProperty(ClientConfiguration.ID, node.id()).withLabel(node.label())
|
||||
.withRepresentationProperty(ClientConfiguration.ID).withBridge(bridgeUID);
|
||||
return Optional.of(discoveryResultBuilder);
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.config;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager.Status;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager.Type;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PhoneConfigurationBuilder} is responsible for holding configuration informations associated the phone
|
||||
* lines (DECT and FXS / landline)
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PhoneConfigurationBuilder {
|
||||
private static final PhoneConfigurationBuilder BUILDER_INSTANCE = new PhoneConfigurationBuilder();
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PhoneConfigurationBuilder.class);
|
||||
|
||||
private PhoneConfigurationBuilder() {
|
||||
}
|
||||
|
||||
public static PhoneConfigurationBuilder getInstance() {
|
||||
return BUILDER_INSTANCE;
|
||||
}
|
||||
|
||||
public DiscoveryResultBuilder configure(ThingUID bridgeUID, Status config) {
|
||||
ThingUID thingUID = new ThingUID(Type.DECT.equals(config.type()) ? THING_TYPE_DECT : THING_TYPE_FXS, bridgeUID,
|
||||
Integer.toString(config.id()));
|
||||
|
||||
logger.debug("Adding new Freebox Phone {} to inbox", thingUID);
|
||||
|
||||
return DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
|
||||
.withProperty(ClientConfiguration.ID, config.id()).withLabel(config.type().name())
|
||||
.withRepresentationProperty(ClientConfiguration.ID);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlayerConfiguration} is responsible for holding configuration informations needed to access/poll the
|
||||
* freebox player
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlayerConfiguration extends ClientConfiguration {
|
||||
public static final String REMOTE_CODE = "remoteCode";
|
||||
public String remoteCode = "";
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.console;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.config.FreeboxOsConfiguration.APP_TOKEN;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants;
|
||||
import org.openhab.binding.freeboxos.internal.handler.FreeboxOsHandler;
|
||||
import org.openhab.core.io.console.Console;
|
||||
import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
|
||||
import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingRegistry;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link FreeboxOsCommandExtension} is responsible for handling console commands
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
@Component(service = ConsoleCommandExtension.class)
|
||||
public class FreeboxOsCommandExtension extends AbstractConsoleCommandExtension {
|
||||
|
||||
private final ThingRegistry thingRegistry;
|
||||
|
||||
@Activate
|
||||
public FreeboxOsCommandExtension(final @Reference ThingRegistry thingRegistry) {
|
||||
super(FreeboxOsBindingConstants.BINDING_ID, "Interact with the Freebox OS binding.");
|
||||
this.thingRegistry = thingRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String[] args, Console console) {
|
||||
if (args.length == 2) {
|
||||
Thing thing = null;
|
||||
try {
|
||||
ThingUID thingUID = new ThingUID(args[0]);
|
||||
thing = thingRegistry.get(thingUID);
|
||||
} catch (IllegalArgumentException e) {
|
||||
thing = null;
|
||||
}
|
||||
ThingHandler thingHandler = null;
|
||||
FreeboxOsHandler handler = null;
|
||||
if (thing != null) {
|
||||
thingHandler = thing.getHandler();
|
||||
if (thingHandler instanceof FreeboxOsHandler) {
|
||||
handler = (FreeboxOsHandler) thingHandler;
|
||||
}
|
||||
}
|
||||
if (thing == null) {
|
||||
console.println("Bad thing id '" + args[0] + "'");
|
||||
printUsage(console);
|
||||
} else if (thingHandler == null) {
|
||||
console.println("No handler initialized for the thing id '" + args[0] + "'");
|
||||
printUsage(console);
|
||||
} else if (handler == null) {
|
||||
console.println("'" + args[0] + "' is not a freebox bridge id");
|
||||
printUsage(console);
|
||||
} else {
|
||||
switch (args[1]) {
|
||||
case APP_TOKEN:
|
||||
String token = handler.getConfiguration().appToken;
|
||||
console.println("Your application token is " + (token.isEmpty() ? "undefined" : token));
|
||||
break;
|
||||
default:
|
||||
printUsage(console);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printUsage(console);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUsages() {
|
||||
return Arrays.asList(buildCommandUsage(String.format("<bridgeUID> %s show the application token", APP_TOKEN)));
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
import static org.openhab.binding.freeboxos.internal.config.FreeboxOsConfiguration.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.jmdns.ServiceInfo;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ApiDiscoveryParticipant} is responsible for discovering the various servers flavors of bridges thing using
|
||||
* mDNS discovery service
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@Component
|
||||
@NonNullByDefault
|
||||
public class ApiDiscoveryParticipant implements MDNSDiscoveryParticipant {
|
||||
private static final String DOMAIN_PROPERTY = "api_domain";
|
||||
private static final String PORT_PROPERTY = "https_port";
|
||||
private static final String HTTPS_PROPERTY = "https_available";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ApiDiscoveryParticipant.class);
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||
return BRIDGE_TYPE_UIDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceType() {
|
||||
return "_fbx-api._tcp.local.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DiscoveryResult createResult(ServiceInfo service) {
|
||||
logger.debug("createResult ServiceInfo: {}", service);
|
||||
ThingUID thingUID = getThingUID(service);
|
||||
return thingUID != null
|
||||
? DiscoveryResultBuilder.create(thingUID).withLabel("Bridge Freebox OS")
|
||||
.withRepresentationProperty(API_DOMAIN)
|
||||
.withProperty(HTTPS_AVAILABLE, "1".equals(service.getPropertyString(HTTPS_PROPERTY)))
|
||||
.withProperty(HTTPS_PORT, service.getPropertyString(PORT_PROPERTY))
|
||||
.withProperty(API_DOMAIN, service.getPropertyString(DOMAIN_PROPERTY)).build()
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingUID getThingUID(ServiceInfo service) {
|
||||
String domain = service.getPropertyString(DOMAIN_PROPERTY);
|
||||
if (domain != null) {
|
||||
String[] elements = domain.split("\\.");
|
||||
if (elements.length > 0) {
|
||||
return new ThingUID(BRIDGE_TYPE_API, elements[0]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,283 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.PermissionException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.APManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.APManager.Station;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.FreeplugManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager.Status;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Player;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.RepeaterManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.RepeaterManager.Repeater;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.SystemManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.SystemManager.Config;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.VmManager;
|
||||
import org.openhab.binding.freeboxos.internal.config.ClientConfiguration;
|
||||
import org.openhab.binding.freeboxos.internal.config.FreeplugConfigurationBuilder;
|
||||
import org.openhab.binding.freeboxos.internal.config.NodeConfigurationBuilder;
|
||||
import org.openhab.binding.freeboxos.internal.config.PhoneConfigurationBuilder;
|
||||
import org.openhab.binding.freeboxos.internal.handler.FreeboxOsHandler;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link FreeboxOsDiscoveryService} is responsible for discovering all things
|
||||
* except the Freebox API thing itself
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FreeboxOsDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
|
||||
private static final int DISCOVERY_TIME_SECONDS = 10;
|
||||
private static final int BACKGROUND_SCAN_REFRESH_MINUTES = 1;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FreeboxOsDiscoveryService.class);
|
||||
|
||||
private Optional<ScheduledFuture<?>> backgroundFuture = Optional.empty();
|
||||
private @Nullable FreeboxOsHandler bridgeHandler;
|
||||
|
||||
public FreeboxOsDiscoveryService() {
|
||||
super(Stream.of(THINGS_TYPES_UIDS, HOME_TYPES_UIDS).flatMap(Set::stream).collect(Collectors.toSet()),
|
||||
DISCOVERY_TIME_SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof FreeboxOsHandler) {
|
||||
bridgeHandler = (FreeboxOsHandler) handler;
|
||||
activate(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return bridgeHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
stopBackgroundDiscovery();
|
||||
backgroundFuture = Optional.of(scheduler.scheduleWithFixedDelay(this::startScan,
|
||||
BACKGROUND_SCAN_REFRESH_MINUTES, BACKGROUND_SCAN_REFRESH_MINUTES, TimeUnit.MINUTES));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
backgroundFuture.ifPresent(future -> future.cancel(true));
|
||||
backgroundFuture = Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.debug("Starting Freebox discovery scan");
|
||||
FreeboxOsHandler localHandler = bridgeHandler;
|
||||
if (localHandler != null && localHandler.getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
try {
|
||||
ThingUID bridgeUID = localHandler.getThing().getUID();
|
||||
|
||||
List<LanHost> lanHosts = localHandler.getManager(LanBrowserManager.class).getHosts().stream()
|
||||
.filter(LanHost::reachable).collect(Collectors.toList());
|
||||
|
||||
discoverServer(localHandler.getManager(SystemManager.class), bridgeUID);
|
||||
discoverPhone(localHandler.getManager(PhoneManager.class), bridgeUID);
|
||||
discoverPlugs(localHandler.getManager(FreeplugManager.class), bridgeUID);
|
||||
discoverRepeater(localHandler.getManager(RepeaterManager.class), bridgeUID, lanHosts);
|
||||
discoverPlayer(localHandler.getManager(PlayerManager.class), bridgeUID, lanHosts);
|
||||
discoverVM(localHandler.getManager(VmManager.class), bridgeUID, lanHosts);
|
||||
discoverHome(localHandler.getManager(HomeManager.class), bridgeUID);
|
||||
if (localHandler.getConfiguration().discoverNetDevice) {
|
||||
discoverHosts(localHandler, bridgeUID, lanHosts);
|
||||
}
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error while requesting data for things discovery: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverHome(HomeManager homeManager, ThingUID bridgeUID) throws FreeboxException {
|
||||
NodeConfigurationBuilder builder = NodeConfigurationBuilder.getInstance();
|
||||
try {
|
||||
homeManager.getHomeNodes().forEach(
|
||||
node -> builder.configure(bridgeUID, node).ifPresent(result -> thingDiscovered(result.build())));
|
||||
} catch (PermissionException e) {
|
||||
logger.warn("Missing permission to discover Home {}", e.getPermission());
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverPlugs(FreeplugManager freeplugManager, ThingUID bridgeUID) {
|
||||
FreeplugConfigurationBuilder builder = FreeplugConfigurationBuilder.getInstance();
|
||||
try {
|
||||
freeplugManager.getPlugs().forEach(plug -> thingDiscovered(builder.configure(bridgeUID, plug).build()));
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error discovering freeplugs {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverPhone(PhoneManager phoneManager, ThingUID bridgeUID) throws FreeboxException {
|
||||
PhoneConfigurationBuilder builder = PhoneConfigurationBuilder.getInstance();
|
||||
List<Status> statuses = List.of();
|
||||
try {
|
||||
statuses = phoneManager.getPhoneStatuses();
|
||||
statuses.forEach(phone -> thingDiscovered(builder.configure(bridgeUID, phone).build()));
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error discovering phones {}", e.getMessage());
|
||||
}
|
||||
if (!statuses.isEmpty()) {
|
||||
ThingUID thingUID = new ThingUID(THING_TYPE_CALL, bridgeUID, "landline");
|
||||
logger.debug("Adding new Call thing {} to inbox", thingUID);
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
|
||||
.withLabel("Freebox Calls").build();
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverHosts(FreeboxOsHandler localHandler, ThingUID bridgeUID, List<LanHost> lanHosts)
|
||||
throws FreeboxException {
|
||||
try {
|
||||
List<MACAddress> wifiMacs = new ArrayList<>();
|
||||
wifiMacs.addAll(localHandler.getManager(APManager.class).getStations().stream().map(Station::mac)
|
||||
.collect(Collectors.toList()));
|
||||
wifiMacs.addAll(localHandler.getManager(RepeaterManager.class).getHosts().stream().map(LanHost::getMac)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
lanHosts.forEach(lanHost -> {
|
||||
MACAddress mac = lanHost.getMac();
|
||||
String macString = mac.toColonDelimitedString();
|
||||
ThingUID thingUID = new ThingUID(wifiMacs.contains(mac) ? THING_TYPE_WIFI_HOST : THING_TYPE_HOST,
|
||||
bridgeUID, mac.toHexString(false));
|
||||
logger.debug("Adding new Freebox Network Host {} to inbox", thingUID);
|
||||
DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
|
||||
.withLabel(lanHost.getPrimaryName().orElse("Network Device %s".formatted(macString)))
|
||||
.withTTL(300).withProperty(Thing.PROPERTY_MAC_ADDRESS, macString)
|
||||
.withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS);
|
||||
thingDiscovered(builder.build());
|
||||
});
|
||||
} catch (PermissionException e) {
|
||||
logger.warn("Missing permission to discover Hosts {}", e.getPermission());
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverVM(VmManager vmManager, ThingUID bridgeUID, List<LanHost> lanHosts) throws FreeboxException {
|
||||
try {
|
||||
vmManager.getDevices().forEach(vm -> {
|
||||
MACAddress mac = vm.mac();
|
||||
lanHosts.removeIf(host -> host.getMac().equals(mac));
|
||||
|
||||
ThingUID thingUID = new ThingUID(THING_TYPE_VM, bridgeUID, mac.toHexString(false));
|
||||
logger.debug("Adding new VM Device {} to inbox", thingUID);
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
|
||||
.withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS)
|
||||
.withLabel("%s (VM)".formatted(vm.name())).withProperty(ClientConfiguration.ID, vm.id())
|
||||
.withProperty(Thing.PROPERTY_MAC_ADDRESS, mac.toColonDelimitedString()).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
});
|
||||
} catch (PermissionException e) {
|
||||
logger.warn("Missing permission to discover VM {}", e.getPermission());
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverRepeater(RepeaterManager repeaterManager, ThingUID bridgeUID, List<LanHost> lanHosts)
|
||||
throws FreeboxException {
|
||||
try {
|
||||
List<Repeater> repeaters = repeaterManager.getDevices();
|
||||
repeaters.forEach(repeater -> {
|
||||
MACAddress mac = repeater.mainMac();
|
||||
lanHosts.removeIf(host -> host.getMac().equals(mac));
|
||||
|
||||
ThingUID thingUID = new ThingUID(THING_TYPE_REPEATER, bridgeUID, Integer.toString(repeater.id()));
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
|
||||
.withLabel("Repeater %s".formatted(repeater.name()))
|
||||
.withProperty(Thing.PROPERTY_MAC_ADDRESS, mac.toColonDelimitedString())
|
||||
.withProperty(ClientConfiguration.ID, repeater.id())
|
||||
.withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
});
|
||||
} catch (PermissionException e) {
|
||||
logger.warn("Missing permission to discover Repeater {}", e.getPermission());
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverServer(SystemManager systemManager, ThingUID bridgeUID) throws FreeboxException {
|
||||
try {
|
||||
Config config = systemManager.getConfig();
|
||||
|
||||
ThingTypeUID targetType = config.boardName().startsWith("fbxgw7") ? THING_TYPE_DELTA
|
||||
: THING_TYPE_REVOLUTION;
|
||||
ThingUID thingUID = new ThingUID(targetType, bridgeUID, config.serial());
|
||||
logger.debug("Adding new Freebox Server {} to inbox", thingUID);
|
||||
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
|
||||
.withProperty(Thing.PROPERTY_MAC_ADDRESS, config.mac())
|
||||
.withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).withLabel(config.modelInfo().prettyName())
|
||||
.build();
|
||||
thingDiscovered(discoveryResult);
|
||||
} catch (PermissionException e) {
|
||||
logger.warn("Missing permission to discover Server {}", e.getPermission());
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverPlayer(PlayerManager playerManager, ThingUID bridgeUID, List<LanHost> lanHosts)
|
||||
throws FreeboxException {
|
||||
try {
|
||||
for (Player player : playerManager.getDevices()) {
|
||||
lanHosts.removeIf(host -> host.getMac().equals(player.mac()));
|
||||
ThingUID thingUID = new ThingUID(player.apiAvailable() ? THING_TYPE_ACTIVE_PLAYER : THING_TYPE_PLAYER,
|
||||
bridgeUID, Integer.toString(player.id()));
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
|
||||
.withLabel(player.deviceName())
|
||||
.withProperty(Thing.PROPERTY_MAC_ADDRESS, player.mac().toColonDelimitedString())
|
||||
.withProperty(ClientConfiguration.ID, player.id())
|
||||
.withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
} catch (PermissionException e) {
|
||||
logger.warn("Missing permission to discover Player {}", e.getPermission());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.action.ActivePlayerActions;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Configuration;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.ForegroundApp;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Player;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Status;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ActivePlayerHandler} is responsible for handling everything associated to Freebox Player with api
|
||||
* capabilities.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ActivePlayerHandler extends PlayerHandler implements FreeDeviceIntf {
|
||||
private final Logger logger = LoggerFactory.getLogger(ActivePlayerHandler.class);
|
||||
|
||||
private final ChannelUID eventChannelUID;
|
||||
|
||||
private long uptime = -1;
|
||||
|
||||
public ActivePlayerHandler(Thing thing) {
|
||||
super(thing);
|
||||
eventChannelUID = new ChannelUID(getThing().getUID(), SYS_INFO, BOX_EVENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initializeProperties(Map<String, String> properties) throws FreeboxException {
|
||||
super.initializeProperties(properties);
|
||||
Player player = getManager(PlayerManager.class).getDevice(getClientId());
|
||||
if (player.reachable()) {
|
||||
Configuration config = getManager(PlayerManager.class).getConfig(player.id());
|
||||
properties.put(Thing.PROPERTY_SERIAL_NUMBER, config.serial());
|
||||
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, config.firmwareVersion());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
super.internalPoll();
|
||||
if (thing.getStatus().equals(ThingStatus.ONLINE)) {
|
||||
Status status = getManager(PlayerManager.class).getPlayerStatus(getClientId());
|
||||
updateChannelString(PLAYER_STATUS, PLAYER_STATUS, status.powerState().name());
|
||||
ForegroundApp foreground = status.foregroundApp();
|
||||
if (foreground != null) {
|
||||
updateChannelString(PLAYER_STATUS, PACKAGE, foreground._package());
|
||||
}
|
||||
Configuration config = getManager(PlayerManager.class).getConfig(getClientId());
|
||||
|
||||
uptime = checkUptimeAndFirmware(config.uptimeVal(), uptime, config.firmwareVersion());
|
||||
updateChannelQuantity(SYS_INFO, UPTIME, uptime, Units.SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
public void reboot() {
|
||||
processReboot(() -> {
|
||||
try {
|
||||
getManager(PlayerManager.class).reboot(getClientId());
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error rebooting: {}", e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singletonList(ActivePlayerActions.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelUID getEventChannelUID() {
|
||||
return eventChannelUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerChannel(ChannelUID channelUID, String event) {
|
||||
super.triggerChannel(channelUID, event);
|
||||
}
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.core.audio.AudioFormat.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.MediaReceiverManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.MediaReceiverManager.Action;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.MediaReceiverManager.MediaType;
|
||||
import org.openhab.core.audio.AudioFormat;
|
||||
import org.openhab.core.audio.AudioHTTPServer;
|
||||
import org.openhab.core.audio.AudioSinkAsync;
|
||||
import org.openhab.core.audio.AudioStream;
|
||||
import org.openhab.core.audio.StreamServed;
|
||||
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.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link AirMediaSink} is holding AudioSink capabilities for various
|
||||
* things.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AirMediaSink extends AudioSinkAsync {
|
||||
private static final Set<Class<? extends AudioStream>> SUPPORTED_STREAMS = Set.of(AudioStream.class);
|
||||
private static final Set<AudioFormat> BASIC_FORMATS = Set.of(WAV, OGG);
|
||||
private static final Set<AudioFormat> ALL_MP3_FORMATS = Set.of(
|
||||
new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 96000, null),
|
||||
new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 112000, null),
|
||||
new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 128000, null),
|
||||
new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 160000, null),
|
||||
new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 192000, null),
|
||||
new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 224000, null),
|
||||
new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 256000, null),
|
||||
new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 320000, null));
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AirMediaSink.class);
|
||||
private final ApiConsumerHandler thingHandler;
|
||||
private final Set<AudioFormat> supportedFormats = new HashSet<>();
|
||||
private final AudioHTTPServer audioHTTPServer;
|
||||
private final String callbackUrl;
|
||||
private final String playerName;
|
||||
private final String password;
|
||||
|
||||
public AirMediaSink(ApiConsumerHandler thingHandler, AudioHTTPServer audioHTTPServer, String callbackUrl,
|
||||
String playerName, String password, boolean acceptAllMp3) {
|
||||
this.thingHandler = thingHandler;
|
||||
this.audioHTTPServer = audioHTTPServer;
|
||||
this.playerName = playerName;
|
||||
this.callbackUrl = callbackUrl;
|
||||
this.password = password;
|
||||
|
||||
supportedFormats.addAll(BASIC_FORMATS);
|
||||
if (acceptAllMp3) {
|
||||
supportedFormats.addAll(ALL_MP3_FORMATS);
|
||||
} else { // Only accept MP3 bitrates >= 96 kbps
|
||||
supportedFormats.add(MP3);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<? extends AudioStream>> getSupportedStreams() {
|
||||
return SUPPORTED_STREAMS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PercentType getVolume() throws IOException {
|
||||
logger.debug("getVolume received but AirMedia does not have the capability - returning 100%.");
|
||||
return PercentType.HUNDRED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolume(PercentType volume) throws IOException {
|
||||
logger.debug("setVolume received but AirMedia does not have the capability - ignoring it.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return thingHandler.getThing().getUID().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getLabel(@Nullable Locale locale) {
|
||||
return thingHandler.getThing().getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processAsynchronously(@Nullable AudioStream audioStream)
|
||||
throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
|
||||
if (thingHandler.getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
try {
|
||||
MediaReceiverManager manager = thingHandler.getManager(MediaReceiverManager.class);
|
||||
if (audioStream == null) {
|
||||
manager.sendToReceiver(playerName, password, Action.STOP, MediaType.VIDEO);
|
||||
return;
|
||||
}
|
||||
|
||||
if (audioStream instanceof URLAudioStream urlAudioStream) {
|
||||
// it is an external URL, we can access it directly
|
||||
logger.debug("AirPlay audio sink: process url {}", urlAudioStream.getURL());
|
||||
playMedia(manager, urlAudioStream.getURL());
|
||||
return;
|
||||
}
|
||||
// we serve it on our own HTTP server
|
||||
StreamServed streamServed;
|
||||
try {
|
||||
streamServed = audioHTTPServer.serve(audioStream, 5, true);
|
||||
} catch (IOException e) {
|
||||
try {
|
||||
audioStream.close();
|
||||
} catch (IOException ex) {
|
||||
logger.debug("Exception while closing audioStream");
|
||||
}
|
||||
throw new UnsupportedAudioStreamException(
|
||||
"AirPlay device was not able to handle the audio stream (cache on disk failed).",
|
||||
audioStream.getClass(), e);
|
||||
}
|
||||
streamServed.playEnd().thenRun(() -> this.playbackFinished(audioStream));
|
||||
logger.debug("AirPlay audio sink: process url {}", callbackUrl + streamServed.url());
|
||||
playMedia(manager, callbackUrl + streamServed.url());
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Audio stream playback failed: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void playMedia(MediaReceiverManager manager, String url) throws FreeboxException {
|
||||
manager.sendToReceiver(playerName, password, Action.STOP, MediaType.VIDEO);
|
||||
manager.sendToReceiver(playerName, password, Action.START, MediaType.VIDEO, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AudioFormat> getSupportedFormats() {
|
||||
return supportedFormats;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.EndpointState;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* The {@link AlarmHandler} is responsible for handling everything associated to
|
||||
* any Freebox Home Alarm thing type.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AlarmHandler extends HomeNodeHandler {
|
||||
|
||||
public AlarmHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State getChannelState(HomeManager homeManager, String channelId, EndpointState state) {
|
||||
String value = state.value();
|
||||
|
||||
if (value == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
|
||||
return switch (channelId) {
|
||||
case NODE_BATTERY -> DecimalType.valueOf(value);
|
||||
case ALARM_PIN -> StringType.valueOf(value);
|
||||
case ALARM_SOUND, ALARM_VOLUME -> QuantityType.valueOf(value + " %");
|
||||
case ALARM_TIMEOUT1, ALARM_TIMEOUT2, ALARM_TIMEOUT3 -> QuantityType.valueOf(value + " s");
|
||||
default -> UnDefType.NULL;
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,352 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.Source;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.MediaReceiverManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.MediaReceiverManager.MediaType;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.MediaReceiverManager.Receiver;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.RestManager;
|
||||
import org.openhab.binding.freeboxos.internal.config.ApiConsumerConfiguration;
|
||||
import org.openhab.binding.freeboxos.internal.config.ClientConfiguration;
|
||||
import org.openhab.core.audio.AudioSink;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.BridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import inet.ipaddr.IPAddress;
|
||||
import inet.ipaddr.MACAddressString;
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link ServerHandler} is a base abstract class for all devices made available by the FreeboxOs bridge
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
abstract class ApiConsumerHandler extends BaseThingHandler implements ApiConsumerIntf {
|
||||
private final Logger logger = LoggerFactory.getLogger(ApiConsumerHandler.class);
|
||||
private final Map<String, ScheduledFuture<?>> jobs = new HashMap<>();
|
||||
|
||||
private @Nullable ServiceRegistration<?> reg;
|
||||
|
||||
ApiConsumerHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
FreeboxOsHandler bridgeHandler = checkBridgeHandler();
|
||||
if (bridgeHandler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String> properties = editProperties();
|
||||
if (properties.isEmpty()) {
|
||||
try {
|
||||
initializeProperties(properties);
|
||||
checkAirMediaCapabilities(properties);
|
||||
updateProperties(properties);
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error getting thing {} properties: {}", thing.getUID(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
boolean isAudioReceiver = Boolean.parseBoolean(properties.get(MediaType.AUDIO.name()));
|
||||
if (isAudioReceiver) {
|
||||
configureMediaSink(bridgeHandler, properties.getOrDefault(Source.UPNP.name(), ""));
|
||||
}
|
||||
|
||||
startRefreshJob();
|
||||
}
|
||||
|
||||
private void configureMediaSink(FreeboxOsHandler bridgeHandler, String upnpName) {
|
||||
try {
|
||||
Receiver receiver = getManager(MediaReceiverManager.class).getReceiver(upnpName);
|
||||
if (receiver != null && reg == null) {
|
||||
ApiConsumerConfiguration config = getConfig().as(ApiConsumerConfiguration.class);
|
||||
String callbackURL = bridgeHandler.getCallbackURL();
|
||||
if (!config.password.isEmpty() || !receiver.passwordProtected()) {
|
||||
reg = bridgeHandler.getBundleContext().registerService(
|
||||
AudioSink.class.getName(), new AirMediaSink(this, bridgeHandler.getAudioHTTPServer(),
|
||||
callbackURL, receiver.name(), config.password, config.acceptAllMp3),
|
||||
new Hashtable<>());
|
||||
} else {
|
||||
logger.info("A password needs to be configured to enable Air Media capability.");
|
||||
}
|
||||
}
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Unable to retrieve Media Receivers: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends RestManager> T getManager(Class<T> clazz) throws FreeboxException {
|
||||
FreeboxOsHandler handler = checkBridgeHandler();
|
||||
if (handler != null) {
|
||||
return handler.getManager(clazz);
|
||||
}
|
||||
throw new FreeboxException("Bridge handler not yet defined");
|
||||
}
|
||||
|
||||
abstract void initializeProperties(Map<String, String> properties) throws FreeboxException;
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
if (checkBridgeHandler() != null) {
|
||||
startRefreshJob();
|
||||
} else {
|
||||
stopJobs();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType || getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (checkBridgeHandler() == null || !internalHandleCommand(channelUID.getIdWithoutGroup(), command)) {
|
||||
logger.debug("Unexpected command {} on channel {}", command, channelUID.getId());
|
||||
}
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error handling command: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAirMediaCapabilities(Map<String, String> properties) throws FreeboxException {
|
||||
String upnpName = properties.getOrDefault(Source.UPNP.name(), "");
|
||||
Receiver receiver = getManager(MediaReceiverManager.class).getReceiver(upnpName);
|
||||
if (receiver != null) {
|
||||
receiver.capabilities().entrySet()
|
||||
.forEach(entry -> properties.put(entry.getKey().name(), entry.getValue().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable FreeboxOsHandler checkBridgeHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null) {
|
||||
BridgeHandler handler = bridge.getHandler();
|
||||
if (handler instanceof FreeboxOsHandler) {
|
||||
if (bridge.getStatus() == ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
return (FreeboxOsHandler) handler;
|
||||
}
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_MISSING_ERROR);
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
stopJobs();
|
||||
ServiceRegistration<?> localReg = reg;
|
||||
if (localReg != null) {
|
||||
localReg.unregister();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private void startRefreshJob() {
|
||||
removeJob("GlobalJob");
|
||||
|
||||
int refreshInterval = getConfigAs(ApiConsumerConfiguration.class).refreshInterval;
|
||||
logger.debug("Scheduling state update every {} seconds for thing {}...", refreshInterval, getThing().getUID());
|
||||
|
||||
ThingStatusDetail detail = thing.getStatusInfo().getStatusDetail();
|
||||
if (ThingStatusDetail.DUTY_CYCLE.equals(detail)) {
|
||||
try {
|
||||
internalPoll();
|
||||
} catch (FreeboxException ignore) {
|
||||
// An exception is normal if the box is rebooting then let's try again later...
|
||||
addJob("Initialize", this::initialize, 10, TimeUnit.SECONDS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
addJob("GlobalJob", () -> {
|
||||
try {
|
||||
internalPoll();
|
||||
} catch (FreeboxException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}, 0, refreshInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void removeJob(String name) {
|
||||
ScheduledFuture<?> existing = jobs.get(name);
|
||||
if (existing != null && !existing.isCancelled()) {
|
||||
existing.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addJob(String name, Runnable command, long initialDelay, long delay, TimeUnit unit) {
|
||||
removeJob(name);
|
||||
jobs.put(name, scheduler.scheduleWithFixedDelay(command, initialDelay, delay, unit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addJob(String name, Runnable command, long delay, TimeUnit unit) {
|
||||
removeJob(name);
|
||||
jobs.put(name, scheduler.schedule(command, delay, unit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopJobs() {
|
||||
jobs.keySet().forEach(name -> removeJob(name));
|
||||
jobs.clear();
|
||||
}
|
||||
|
||||
protected boolean internalHandleCommand(String channelId, Command command) throws FreeboxException {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract void internalPoll() throws FreeboxException;
|
||||
|
||||
private void updateIfActive(String group, String channelId, State state) {
|
||||
ChannelUID id = new ChannelUID(getThing().getUID(), group, channelId);
|
||||
if (isLinked(id)) {
|
||||
updateState(id, state);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateIfActive(String channelId, State state) {
|
||||
ChannelUID id = new ChannelUID(getThing().getUID(), channelId);
|
||||
if (isLinked(id)) {
|
||||
updateState(id, state);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateChannelDateTimeState(String channelId, @Nullable ZonedDateTime timestamp) {
|
||||
updateIfActive(channelId, timestamp == null ? UnDefType.NULL : new DateTimeType(timestamp));
|
||||
}
|
||||
|
||||
protected void updateChannelDateTimeState(String group, String channelId, @Nullable ZonedDateTime timestamp) {
|
||||
updateIfActive(group, channelId, timestamp == null ? UnDefType.NULL : new DateTimeType(timestamp));
|
||||
}
|
||||
|
||||
protected void updateChannelOnOff(String group, String channelId, boolean value) {
|
||||
updateIfActive(group, channelId, OnOffType.from(value));
|
||||
}
|
||||
|
||||
protected void updateChannelOnOff(String channelId, boolean value) {
|
||||
updateIfActive(channelId, OnOffType.from(value));
|
||||
}
|
||||
|
||||
protected void updateChannelString(String group, String channelId, @Nullable String value) {
|
||||
updateIfActive(group, channelId, value != null ? new StringType(value) : UnDefType.NULL);
|
||||
}
|
||||
|
||||
protected void updateChannelString(String group, String channelId, @Nullable IPAddress value) {
|
||||
updateIfActive(group, channelId, value != null ? new StringType(value.toCanonicalString()) : UnDefType.NULL);
|
||||
}
|
||||
|
||||
protected void updateChannelString(String channelId, @Nullable String value) {
|
||||
updateIfActive(channelId, value != null ? new StringType(value) : UnDefType.NULL);
|
||||
}
|
||||
|
||||
protected void updateChannelString(String channelId, Enum<?> value) {
|
||||
updateIfActive(channelId, new StringType(value.name()));
|
||||
}
|
||||
|
||||
protected void updateChannelString(String group, String channelId, Enum<?> value) {
|
||||
updateIfActive(group, channelId, new StringType(value.name()));
|
||||
}
|
||||
|
||||
protected void updateChannelQuantity(String group, String channelId, double d, Unit<?> unit) {
|
||||
updateChannelQuantity(group, channelId, new QuantityType<>(d, unit));
|
||||
}
|
||||
|
||||
protected void updateChannelQuantity(String channelId, @Nullable QuantityType<?> quantity) {
|
||||
updateIfActive(channelId, quantity != null ? quantity : UnDefType.NULL);
|
||||
}
|
||||
|
||||
protected void updateChannelQuantity(String group, String channelId, @Nullable QuantityType<?> quantity) {
|
||||
updateIfActive(group, channelId, quantity != null ? quantity : UnDefType.NULL);
|
||||
}
|
||||
|
||||
protected void updateChannelDecimal(String group, String channelId, @Nullable Integer value) {
|
||||
updateIfActive(group, channelId, value != null ? new DecimalType(value) : UnDefType.NULL);
|
||||
}
|
||||
|
||||
protected void updateChannelQuantity(String group, String channelId, QuantityType<?> qtty, Unit<?> unit) {
|
||||
updateChannelQuantity(group, channelId, qtty.toUnit(unit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
|
||||
super.updateStatus(status, statusDetail, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> editProperties() {
|
||||
return super.editProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProperties(@Nullable Map<String, String> properties) {
|
||||
super.updateProperties(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration getConfig() {
|
||||
return super.getConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getClientId() {
|
||||
return ((BigDecimal) getConfig().get(ClientConfiguration.ID)).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MACAddress getMac() {
|
||||
String mac = (String) getConfig().get(Thing.PROPERTY_MAC_ADDRESS);
|
||||
return new MACAddressString(mac).getAddress();
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.config.ClientConfiguration;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
|
||||
import inet.ipaddr.MACAddressString;
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link ApiConsumerIntf} defines some common methods for various devices (server, player, repeater) not belonging
|
||||
* to the same class hierarchy
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface ApiConsumerIntf extends ThingHandler {
|
||||
|
||||
Map<String, String> editProperties();
|
||||
|
||||
Configuration getConfig();
|
||||
|
||||
void updateProperties(@Nullable Map<String, String> properties);
|
||||
|
||||
void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description);
|
||||
|
||||
void stopJobs();
|
||||
|
||||
void addJob(String name, Runnable command, long initialDelay, long delay, TimeUnit unit);
|
||||
|
||||
void addJob(String name, Runnable command, long delay, TimeUnit unit);
|
||||
|
||||
default int getClientId() {
|
||||
return ((BigDecimal) getConfig().get(ClientConfiguration.ID)).intValue();
|
||||
}
|
||||
|
||||
default MACAddress getMac() {
|
||||
String mac = (String) getConfig().get(Thing.PROPERTY_MAC_ADDRESS);
|
||||
return new MACAddressString(mac).getAddress();
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.Endpoint;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.EndpointState;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* The {@link BasicShutterHandler} is responsible for handling everything associated to
|
||||
* any Freebox Home basic-shutter thing type.
|
||||
*
|
||||
* @author ben12 - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BasicShutterHandler extends HomeNodeHandler {
|
||||
private static final Set<String> SHUTTER_ENDPOINTS = Set.of(SHUTTER_STOP, BASIC_SHUTTER_UP, BASIC_SHUTTER_DOWN);
|
||||
|
||||
public BasicShutterHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalConfigureChannel(String channelId, Configuration conf, List<Endpoint> endpoints) {
|
||||
endpoints.stream().filter(ep -> channelId.equals(BASIC_SHUTTER_STATE) && SHUTTER_ENDPOINTS.contains(ep.name()))
|
||||
.forEach(endPoint -> conf.put(endPoint.name(), endPoint.id()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State getChannelState(HomeManager homeManager, String channelId, EndpointState state) {
|
||||
String value = state.value();
|
||||
return value != null && channelId.equals(BASIC_SHUTTER_STATE)
|
||||
? state.asBoolean() ? OpenClosedType.CLOSED : OpenClosedType.OPEN
|
||||
: UnDefType.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean executeChannelCommand(HomeManager homeManager, String channelId, Command command,
|
||||
Configuration config) throws FreeboxException {
|
||||
Integer slot = getSlotId(config, command.toString().toLowerCase());
|
||||
if (BASIC_SHUTTER_STATE.equals(channelId) && slot != null) {
|
||||
return homeManager.putCommand(getClientId(), slot, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.action.CallActions;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.CallManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.CallManager.Call;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.CallManager.Type;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link CallHandler} is responsible for handling everything associated to the phone calls received on the box line
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CallHandler extends ApiConsumerHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(CallHandler.class);
|
||||
private final Map<Type, ZonedDateTime> lastCalls = new HashMap<>(Type.values().length);
|
||||
|
||||
public CallHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initializeProperties(Map<String, String> properties) throws FreeboxException {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
logger.debug("Polling phone calls ...");
|
||||
|
||||
lastCalls.clear();
|
||||
|
||||
List<Call> entries = getManager(CallManager.class).getCallEntries();
|
||||
Arrays.stream(Type.values()).forEach(callType -> entries.stream().filter(call -> call.type().equals(callType))
|
||||
.reduce((first, second) -> second).ifPresent(this::updateCallChannels));
|
||||
|
||||
// Clear incoming call if the youngest is not an incoming call
|
||||
lastCalls.entrySet().stream().sorted(Map.Entry.comparingByValue()).reduce((first, second) -> second)
|
||||
.map(entry -> entry.getKey()).filter(type -> !Type.INCOMING.equals(type)).ifPresent(type -> {
|
||||
String groupName = Type.INCOMING.name().toLowerCase();
|
||||
getThing().getChannelsOfGroup(groupName).stream().map(Channel::getUID).filter(uid -> isLinked(uid))
|
||||
.forEach(uid -> updateState(uid, UnDefType.NULL));
|
||||
});
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
private void updateCallChannels(Call call) {
|
||||
Type lastType = call.type();
|
||||
lastCalls.put(lastType, call.datetime());
|
||||
String group = lastType.name().toLowerCase();
|
||||
String phoneNumber = call.number();
|
||||
|
||||
updateChannelString(group, NUMBER, phoneNumber);
|
||||
updateChannelString(group, NAME, call.name());
|
||||
updateChannelDateTimeState(group, TIMESTAMP, call.datetime());
|
||||
|
||||
// Do not consider duration for Missed & incoming calls
|
||||
if (lastType == Type.ACCEPTED || lastType == Type.OUTGOING) {
|
||||
updateChannelQuantity(group, DURATION, call.duration(), Units.SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
public void emptyQueue() {
|
||||
try {
|
||||
getManager(CallManager.class).emptyQueue();
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error clearing call logs: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(CallActions.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link CameraHandler} is responsible for handling everything associated to
|
||||
* any Freebox Home Camera thing type.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CameraHandler extends ApiConsumerHandler {
|
||||
|
||||
public CameraHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initializeProperties(Map<String, String> properties) throws FreeboxException {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean internalHandleCommand(String channelId, Command command) throws FreeboxException {
|
||||
return super.internalHandleCommand(channelId, command);
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager.Config;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager.Status;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link DectHandler} is responsible for handling DECT specifics of the Telephony API
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DectHandler extends FxsHandler {
|
||||
|
||||
public DectHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateConfigChannels(Config config) {
|
||||
super.updateConfigChannels(config);
|
||||
updateChannelOnOff(DECT_ACTIVE, config.dectEnabled());
|
||||
updateChannelOnOff(ALTERNATE_RING, config.dectRingOnOff());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateStatusChannels(Status status) {
|
||||
super.updateStatusChannels(status);
|
||||
updateIfActive(GAIN_RX, new PercentType(status.gainRx()));
|
||||
updateIfActive(GAIN_TX, new PercentType(status.gainTx()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean internalHandleCommand(String channelId, Command command) throws FreeboxException {
|
||||
PhoneManager phoneManager = getManager(PhoneManager.class);
|
||||
if (command instanceof OnOffType) {
|
||||
boolean status = OnOffType.ON.equals(command);
|
||||
if (RINGING.equals(channelId)) {
|
||||
phoneManager.ringDect(status);
|
||||
return true;
|
||||
} else if (DECT_ACTIVE.equals(channelId)) {
|
||||
phoneManager.setStatus(status);
|
||||
return true;
|
||||
} else if (ALTERNATE_RING.equals(channelId)) {
|
||||
phoneManager.alternateRing(status);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (command instanceof PercentType) {
|
||||
PercentType percent = (PercentType) command;
|
||||
if (GAIN_RX.equals(channelId)) {
|
||||
phoneManager.setGainRx(getClientId(), percent.intValue());
|
||||
updateIfActive(GAIN_RX, percent);
|
||||
return true;
|
||||
} else if (GAIN_TX.equals(channelId)) {
|
||||
phoneManager.setGainTx(getClientId(), percent.intValue());
|
||||
updateIfActive(GAIN_RX, percent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.internalHandleCommand(channelId, command);
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
|
||||
/**
|
||||
* The {@link FreeDeviceIntf} defines some common methods for various devices (server, player, repeater) not belonging
|
||||
* to the same class hierarchy
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface FreeDeviceIntf extends ApiConsumerIntf {
|
||||
public ChannelUID getEventChannelUID();
|
||||
|
||||
public void triggerChannel(ChannelUID channelUID, String event);
|
||||
|
||||
default long checkUptimeAndFirmware(long newUptime, long oldUptime, String firmwareVersion) {
|
||||
if (newUptime < oldUptime) {
|
||||
triggerChannel(getEventChannelUID(), "restarted");
|
||||
Map<String, String> properties = editProperties();
|
||||
if (!firmwareVersion.equals(properties.get(Thing.PROPERTY_FIRMWARE_VERSION))) {
|
||||
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, firmwareVersion);
|
||||
updateProperties(properties);
|
||||
triggerChannel(getEventChannelUID(), "firmware_updated");
|
||||
}
|
||||
}
|
||||
return newUptime;
|
||||
}
|
||||
|
||||
default void processReboot(Runnable actualReboot) {
|
||||
triggerChannel(getEventChannelUID(), "reboot_requested");
|
||||
actualReboot.run();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DUTY_CYCLE, "System rebooting...");
|
||||
stopJobs();
|
||||
addJob("Initialize", this::initialize, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.RestManager;
|
||||
import org.openhab.binding.freeboxos.internal.config.FreeboxOsConfiguration;
|
||||
import org.openhab.binding.freeboxos.internal.discovery.FreeboxOsDiscoveryService;
|
||||
import org.openhab.core.audio.AudioHTTPServer;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link FreeboxOsHandler} handle common parts of Freebox bridges.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FreeboxOsHandler extends BaseBridgeHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(FreeboxOsHandler.class);
|
||||
private final FreeboxOsSession session;
|
||||
private final String callbackURL;
|
||||
private final BundleContext bundleContext;
|
||||
private final AudioHTTPServer audioHTTPServer;
|
||||
|
||||
private Optional<Future<?>> openConnectionJob = Optional.empty();
|
||||
private Optional<Future<?>> grantingJob = Optional.empty();
|
||||
|
||||
public FreeboxOsHandler(Bridge thing, FreeboxOsSession session, String callbackURL, BundleContext bundleContext,
|
||||
AudioHTTPServer audioHTTPServer) {
|
||||
super(thing);
|
||||
this.session = session;
|
||||
this.callbackURL = callbackURL;
|
||||
this.bundleContext = bundleContext;
|
||||
this.audioHTTPServer = audioHTTPServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
freeConnectionJob();
|
||||
|
||||
FreeboxOsConfiguration config = getConfiguration();
|
||||
openConnectionJob = Optional.of(scheduler.submit(() -> {
|
||||
try {
|
||||
session.initialize(config);
|
||||
if (config.appToken.isBlank()) {
|
||||
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
|
||||
"@text/info-conf-pending");
|
||||
grantingJob = Optional.of(scheduler.schedule(this::processGranting, 2, TimeUnit.SECONDS));
|
||||
return;
|
||||
} else {
|
||||
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE);
|
||||
session.openSession(config.appToken);
|
||||
}
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} catch (FreeboxException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||
} catch (InterruptedException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void processGranting() {
|
||||
try {
|
||||
String appToken = session.grant();
|
||||
if (appToken.isBlank()) {
|
||||
grantingJob = Optional.of(scheduler.schedule(this::processGranting, 2, TimeUnit.SECONDS));
|
||||
} else {
|
||||
Configuration thingConfig = editConfiguration();
|
||||
thingConfig.put(FreeboxOsConfiguration.APP_TOKEN, appToken);
|
||||
updateConfiguration(thingConfig);
|
||||
logger.info("AppToken updated, ensure giving permissions in the Freebox management console");
|
||||
initialize();
|
||||
}
|
||||
} catch (FreeboxException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends RestManager> T getManager(Class<T> clazz) throws FreeboxException {
|
||||
return session.getManager(clazz);
|
||||
}
|
||||
|
||||
private void freeConnectionJob() {
|
||||
openConnectionJob.ifPresent(job -> job.cancel(true));
|
||||
openConnectionJob = Optional.empty();
|
||||
grantingJob.ifPresent(job -> job.cancel(true));
|
||||
grantingJob = Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
freeConnectionJob();
|
||||
session.closeSession();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(FreeboxOsDiscoveryService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
}
|
||||
|
||||
public FreeboxOsConfiguration getConfiguration() {
|
||||
return getConfigAs(FreeboxOsConfiguration.class);
|
||||
}
|
||||
|
||||
public String getCallbackURL() {
|
||||
return callbackURL;
|
||||
}
|
||||
|
||||
public BundleContext getBundleContext() {
|
||||
return bundleContext;
|
||||
}
|
||||
|
||||
public AudioHTTPServer getAudioHTTPServer() {
|
||||
return audioHTTPServer;
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.action.FreeplugActions;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.FreeplugManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.FreeplugManager.NetRole;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link FreeplugHandler} is responsible for handling everything associated to a CPL gateway managed by the freebox
|
||||
* server
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FreeplugHandler extends ApiConsumerHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(FreeplugHandler.class);
|
||||
|
||||
public FreeplugHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initializeProperties(Map<String, String> properties) throws FreeboxException {
|
||||
getManager(FreeplugManager.class).getPlug(getMac()).ifPresent(plug -> {
|
||||
NetRole role = plug.netRole();
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, plug.model());
|
||||
properties.put(ROLE, role.name());
|
||||
properties.put(NET_ID, plug.netId());
|
||||
properties.put(ETHERNET_SPEED, String.format("%d Mb/s", plug.ethSpeed()));
|
||||
properties.put(LOCAL, Boolean.valueOf(plug.local()).toString());
|
||||
properties.put(FULL_DUPLEX, Boolean.valueOf(plug.ethFullDuplex()).toString());
|
||||
|
||||
if (role.equals(NetRole.CCO)) { // Coordinator does not provide rate up or down
|
||||
List<Channel> channels = new ArrayList<>(getThing().getChannels());
|
||||
channels.removeIf(channel -> channel.getUID().getId().contains("rate"));
|
||||
updateThing(editThing().withChannels(channels).build());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
getManager(FreeplugManager.class).getPlug(getMac()).ifPresent(plug -> {
|
||||
ZonedDateTime lastSeen = ZonedDateTime.now().minusSeconds(plug.inactive());
|
||||
updateChannelDateTimeState(LAST_SEEN, lastSeen);
|
||||
|
||||
updateChannelString(LINE_STATUS, plug.ethPortStatus());
|
||||
updateChannelOnOff(REACHABLE, plug.hasNetwork());
|
||||
|
||||
updateRateChannel(RATE + "-down", plug.rxRate());
|
||||
updateRateChannel(RATE + "-up", plug.txRate());
|
||||
});
|
||||
}
|
||||
|
||||
private void updateRateChannel(String channel, int rate) {
|
||||
QuantityType<?> qtty = rate != -1 ? new QuantityType<>(rate, Units.MEGABIT_PER_SECOND) : null;
|
||||
updateChannelQuantity(channel, qtty);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
try {
|
||||
getManager(FreeplugManager.class).reboot(getMac());
|
||||
logger.debug("Freeplug {} succesfully restarted", getMac());
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error restarting freeplug: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(FreeplugActions.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager.Config;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager.Status;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link FxsHandler} is responsible for handling everything associated to the landline associated with the
|
||||
* Freebox Server.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FxsHandler extends ApiConsumerHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(FxsHandler.class);
|
||||
|
||||
public FxsHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initializeProperties(Map<String, String> properties) throws FreeboxException {
|
||||
getManager(PhoneManager.class).getStatus(getClientId())
|
||||
.ifPresent(status -> properties.put(Thing.PROPERTY_VENDOR, status.vendor()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
logger.debug("Polling landline status...");
|
||||
|
||||
Config config = getManager(PhoneManager.class).getConfig();
|
||||
updateConfigChannels(config);
|
||||
|
||||
getManager(PhoneManager.class).getStatus(getClientId()).ifPresent(this::updateStatusChannels);
|
||||
}
|
||||
|
||||
protected void updateConfigChannels(Config config) {
|
||||
updateChannelString(TELEPHONY_SERVICE, config.network());
|
||||
}
|
||||
|
||||
protected void updateStatusChannels(Status status) {
|
||||
updateChannelOnOff(ONHOOK, status.onHook());
|
||||
updateChannelOnOff(RINGING, status.isRinging());
|
||||
updateChannelString(HARDWARE_STATUS, status.hardwareDefect() ? "KO" : "OK");
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean internalHandleCommand(String channelId, Command command) throws FreeboxException {
|
||||
if (RINGING.equals(channelId) && command instanceof OnOffType) {
|
||||
getManager(PhoneManager.class).ringFxs(TRUE_COMMANDS.contains(command));
|
||||
return true;
|
||||
}
|
||||
return super.internalHandleCommand(channelId, command);
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.Endpoint;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.EndpointState;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.EpType;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.HomeNode;
|
||||
import org.openhab.binding.freeboxos.internal.config.ApiConsumerConfiguration;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link HomeNodeHandler} is the base class for handler of home node things.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class HomeNodeHandler extends ApiConsumerHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(HomeNodeHandler.class);
|
||||
|
||||
public HomeNodeHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initializeProperties(Map<String, String> properties) throws FreeboxException {
|
||||
HomeNode node = getManager(HomeManager.class).getHomeNode(getClientId());
|
||||
|
||||
// Gets the lowest refresh time or else, we'll keep configuration default
|
||||
node.showEndpoints().stream().filter(ep -> ep.epType() == EpType.SIGNAL).filter(ep -> ep.refresh() != 0)
|
||||
.min(Comparator.comparing(Endpoint::refresh)).map(Endpoint::refresh).ifPresent(rate -> {
|
||||
Configuration thingConfig = editConfiguration();
|
||||
thingConfig.put(ApiConsumerConfiguration.REFRESH_INTERVAL, Integer.toString(rate / 1000));
|
||||
updateConfiguration(thingConfig);
|
||||
});
|
||||
|
||||
properties.putAll(node.props());
|
||||
|
||||
getThing().getChannels().forEach(channel -> {
|
||||
Configuration conf = channel.getConfiguration();
|
||||
node.type().endpoints().stream().filter(ep -> ep.name().equals(channel.getUID().getIdWithoutGroup()))
|
||||
.forEach(endPoint -> conf.put(endPoint.epType().asConfId(), endPoint.id()));
|
||||
internalConfigureChannel(channel.getUID().getIdWithoutGroup(), conf, node.type().endpoints());
|
||||
});
|
||||
}
|
||||
|
||||
protected void internalConfigureChannel(String channelId, Configuration conf, List<Endpoint> endpoints) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
HomeManager homeManager = getManager(HomeManager.class);
|
||||
getThing().getChannels().stream().filter(channel -> isLinked(channel.getUID())).forEach(channel -> {
|
||||
State result = UnDefType.UNDEF;
|
||||
Integer slotId = getSlotId(channel.getConfiguration(), EpType.SIGNAL.asConfId());
|
||||
if (slotId instanceof Integer) {
|
||||
try {
|
||||
EndpointState state = homeManager.getEndpointsState(getClientId(), slotId);
|
||||
if (state != null) {
|
||||
result = getChannelState(homeManager, channel.getUID().getIdWithoutGroup(), state);
|
||||
} else {
|
||||
result = getChannelState(homeManager, channel.getUID().getIdWithoutGroup());
|
||||
}
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error updating channel: {}", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
result = getChannelState(homeManager, channel.getUID().getIdWithoutGroup());
|
||||
}
|
||||
updateState(channel.getUID(), result);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean internalHandleCommand(String channelId, Command command) throws FreeboxException {
|
||||
Channel channel = getThing().getChannel(channelId);
|
||||
if (channel != null) {
|
||||
Configuration config = channel.getConfiguration();
|
||||
String channelWG = channel.getUID().getIdWithoutGroup();
|
||||
Integer slotId = getSlotId(config, EpType.SLOT.asConfId());
|
||||
HomeManager homeManager = getManager(HomeManager.class);
|
||||
return slotId instanceof Integer ? executeSlotCommand(homeManager, channelWG, command, config, slotId)
|
||||
: executeChannelCommand(homeManager, channelWG, command, config);
|
||||
}
|
||||
return super.internalHandleCommand(channelId, command);
|
||||
}
|
||||
|
||||
protected @Nullable Integer getSlotId(Configuration configuration, String endPoint) {
|
||||
Object slot = configuration.get(endPoint);
|
||||
return slot instanceof BigDecimal slotId ? slotId.intValue() : null;
|
||||
}
|
||||
|
||||
protected boolean executeChannelCommand(HomeManager homeManager, String channelId, Command command,
|
||||
Configuration config) throws FreeboxException {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean executeSlotCommand(HomeManager homeManager, String channelId, Command command,
|
||||
Configuration config, int slotId) throws FreeboxException {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected State getChannelState(HomeManager homeManager, String channelWG) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
protected abstract State getChannelState(HomeManager homeManager, String channelId, EndpointState state);
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.action.HostActions;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.HostIntf;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.Source;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.WebSocketManager;
|
||||
import org.openhab.binding.freeboxos.internal.config.ApiConsumerConfiguration;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link HostHandler} is responsible for all network equipments hosted on the network
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HostHandler extends ApiConsumerHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(HostHandler.class);
|
||||
|
||||
// We start in pull mode and switch to push after a first update
|
||||
private boolean pushSubscribed = false;
|
||||
|
||||
public HostHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initializeProperties(Map<String, String> properties) throws FreeboxException {
|
||||
getManager(LanBrowserManager.class).getHost(getMac()).ifPresent(result -> {
|
||||
LanHost host = result.host();
|
||||
properties.put(Thing.PROPERTY_VENDOR, host.vendorName());
|
||||
host.getUPnPName().ifPresent(upnpName -> properties.put(Source.UPNP.name(), upnpName));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
try {
|
||||
getManager(WebSocketManager.class).unregisterListener(getMac());
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error unregistering host from the websocket: {}", e.getMessage());
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
if (pushSubscribed) {
|
||||
return;
|
||||
}
|
||||
HostIntf data = getManager(LanBrowserManager.class).getHost(getMac())
|
||||
.orElseThrow(() -> new FreeboxException("Host data not found"));
|
||||
|
||||
updateConnectivityChannels(data.host());
|
||||
logger.debug("Switching to push mode - refreshInterval will now be ignored for Connectivity data");
|
||||
getManager(WebSocketManager.class).registerListener(data.host().getMac(), this);
|
||||
pushSubscribed = true;
|
||||
}
|
||||
|
||||
public void updateConnectivityChannels(LanHost host) {
|
||||
updateChannelOnOff(CONNECTIVITY, REACHABLE, host.reachable());
|
||||
updateChannelDateTimeState(CONNECTIVITY, LAST_SEEN, host.getLastSeen());
|
||||
updateChannelString(CONNECTIVITY, IP_ADDRESS, host.getIpv4());
|
||||
updateStatus(host.reachable() ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
|
||||
}
|
||||
|
||||
public void wol() {
|
||||
try {
|
||||
getManager(LanBrowserManager.class).wakeOnLan(getMac(),
|
||||
getConfigAs(ApiConsumerConfiguration.class).password);
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error waking up host: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singletonList(HostActions.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.EndpointState;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* The {@link KeyfobHandler} is responsible for handling everything associated to
|
||||
* any Freebox Home keyfob thing type.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class KeyfobHandler extends HomeNodeHandler {
|
||||
|
||||
public KeyfobHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State getChannelState(HomeManager homeManager, String channelId, EndpointState state) {
|
||||
String value = state.value();
|
||||
if (value != null) {
|
||||
switch (channelId) {
|
||||
case KEYFOB_ENABLE:
|
||||
return OnOffType.from(state.asBoolean());
|
||||
case NODE_BATTERY:
|
||||
return DecimalType.valueOf(value);
|
||||
}
|
||||
}
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean executeSlotCommand(HomeManager homeManager, String channelId, Command command,
|
||||
Configuration config, int intValue) throws FreeboxException {
|
||||
if (KEYFOB_ENABLE.equals(channelId) && command instanceof OnOffType onOff) {
|
||||
return getManager(HomeManager.class).putCommand(getClientId(), intValue, onOff);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.KEY_CODE;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.action.PlayerActions;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Player;
|
||||
import org.openhab.binding.freeboxos.internal.config.PlayerConfiguration;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import inet.ipaddr.IPAddress;
|
||||
|
||||
/**
|
||||
* The {@link PlayerHandler} is responsible for handling everything associated to any Freebox Player thing type.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlayerHandler extends HostHandler {
|
||||
private static final List<String> VALID_REMOTE_KEYS = Arrays.asList("red", "green", "blue", "yellow", "power",
|
||||
"list", "tv", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "vol_inc", "vol_dec", "mute", "prgm_inc",
|
||||
"prgm_dec", "prev", "bwd", "play", "rec", "fwd", "next", "up", "right", "down", "left", "back", "swap",
|
||||
"info", "epg", "mail", "media", "help", "options", "pip", "ok", "home");
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PlayerHandler.class);
|
||||
private @Nullable IPAddress ipAddress;
|
||||
|
||||
public PlayerHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initializeProperties(Map<String, String> properties) throws FreeboxException {
|
||||
super.initializeProperties(properties);
|
||||
Player player = getManager(PlayerManager.class).getDevice(getClientId());
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, player.deviceModel().name());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean internalHandleCommand(String channelId, Command command) throws FreeboxException {
|
||||
if (KEY_CODE.equals(channelId) && command instanceof StringType) {
|
||||
sendKey(command.toString(), false, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.internalHandleCommand(channelId, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateConnectivityChannels(LanHost host) {
|
||||
super.updateConnectivityChannels(host);
|
||||
ipAddress = host.getIpv4();
|
||||
}
|
||||
|
||||
public void sendKey(String key, boolean longPress, int count) {
|
||||
String aKey = key.toLowerCase();
|
||||
IPAddress ip = ipAddress;
|
||||
if (ip == null) {
|
||||
logger.warn("Player IP is unknown");
|
||||
} else if (VALID_REMOTE_KEYS.contains(aKey)) {
|
||||
String remoteCode = (String) getConfig().get(PlayerConfiguration.REMOTE_CODE);
|
||||
if (remoteCode != null) {
|
||||
try {
|
||||
getManager(PlayerManager.class).sendKey(ip.toCanonicalString(), remoteCode, aKey, longPress, count);
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error sending key: {}", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.warn("A remote code must be configured in the on the player thing.");
|
||||
}
|
||||
} else {
|
||||
logger.warn("Key '{}' is not a valid key expression", key);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMultipleKeys(String keys) {
|
||||
String[] keyChain = keys.split(",");
|
||||
Arrays.stream(keyChain).forEach(key -> {
|
||||
sendKey(key, false, 1);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singletonList(PlayerActions.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.action.RepeaterActions;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.RepeaterManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.RepeaterManager.Repeater;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link RepeaterHandler} is responsible for interface to a freebox
|
||||
* pop repeater.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RepeaterHandler extends HostHandler implements FreeDeviceIntf {
|
||||
private final Logger logger = LoggerFactory.getLogger(RepeaterHandler.class);
|
||||
private long uptime = -1;
|
||||
private final ChannelUID eventChannelUID;
|
||||
|
||||
public RepeaterHandler(Thing thing) {
|
||||
super(thing);
|
||||
eventChannelUID = new ChannelUID(getThing().getUID(), REPEATER_MISC, BOX_EVENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initializeProperties(Map<String, String> properties) throws FreeboxException {
|
||||
super.initializeProperties(properties);
|
||||
|
||||
Repeater repeater = getManager(RepeaterManager.class).getDevice(getClientId());
|
||||
properties.put(Thing.PROPERTY_SERIAL_NUMBER, repeater.sn());
|
||||
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, repeater.firmwareVersion());
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, repeater.model().name());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
super.internalPoll();
|
||||
|
||||
if (!thing.getStatus().equals(ThingStatus.ONLINE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Polling Repeater status");
|
||||
RepeaterManager repeaterManager = getManager(RepeaterManager.class);
|
||||
|
||||
Repeater repeater = repeaterManager.getDevice(getClientId());
|
||||
updateChannelOnOff(REPEATER_MISC, LED, repeater.ledActivated());
|
||||
updateChannelString(REPEATER_MISC, CONNECTION_STATUS, repeater.connection());
|
||||
|
||||
List<LanHost> hosts = repeaterManager.getRepeaterHosts(getClientId());
|
||||
updateChannelDecimal(REPEATER_MISC, HOST_COUNT, hosts.size());
|
||||
|
||||
uptime = checkUptimeAndFirmware(repeater.getUptimeVal(), uptime, repeater.firmwareVersion());
|
||||
updateChannelQuantity(REPEATER_MISC, UPTIME, uptime, Units.SECOND);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean internalHandleCommand(String channelId, Command command) throws FreeboxException {
|
||||
if (ON_OFF_CLASSES.contains(command.getClass()) && LED.equals(channelId)) {
|
||||
getManager(RepeaterManager.class).led(getClientId(), TRUE_COMMANDS.contains(command))
|
||||
.ifPresent(repeater -> updateChannelOnOff(REPEATER_MISC, LED, repeater.ledActivated()));
|
||||
}
|
||||
return super.internalHandleCommand(channelId, command);
|
||||
}
|
||||
|
||||
public void reboot() {
|
||||
processReboot(() -> {
|
||||
try {
|
||||
getManager(RepeaterManager.class).reboot(getClientId());
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error rebooting: {}", e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(RepeaterActions.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelUID getEventChannelUID() {
|
||||
return eventChannelUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerChannel(ChannelUID channelUID, String event) {
|
||||
super.triggerChannel(channelUID, event);
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
import static org.openhab.core.library.unit.Units.PERCENT;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LcdManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LcdManager.Config;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link RevolutionHandler} is responsible for handling take care of revolution server specifics
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RevolutionHandler extends ServerHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(RevolutionHandler.class);
|
||||
|
||||
public RevolutionHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean internalHandleCommand(String channelId, Command command) throws FreeboxException {
|
||||
LcdManager manager = getManager(LcdManager.class);
|
||||
Config config = manager.getConfig();
|
||||
switch (channelId) {
|
||||
case LCD_BRIGHTNESS:
|
||||
setBrightness(manager, config, command);
|
||||
internalPoll();
|
||||
return true;
|
||||
case LCD_ORIENTATION:
|
||||
setOrientation(manager, config, command);
|
||||
internalPoll();
|
||||
return true;
|
||||
case LCD_FORCED:
|
||||
setForced(manager, config, command);
|
||||
internalPoll();
|
||||
return true;
|
||||
}
|
||||
return super.internalHandleCommand(channelId, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
super.internalPoll();
|
||||
Config config = getManager(LcdManager.class).getConfig();
|
||||
updateChannelQuantity(DISPLAY, LCD_BRIGHTNESS, config.brightness(), PERCENT);
|
||||
updateChannelDecimal(DISPLAY, LCD_ORIENTATION, config.orientation());
|
||||
updateChannelOnOff(DISPLAY, LCD_FORCED, config.orientationForced());
|
||||
}
|
||||
|
||||
private void setOrientation(LcdManager manager, Config config, Command command) throws FreeboxException {
|
||||
if (command instanceof DecimalType) {
|
||||
manager.setOrientation(((DecimalType) command).intValue());
|
||||
} else {
|
||||
logger.warn("Invalid command {} from channel {}", command, LCD_ORIENTATION);
|
||||
}
|
||||
}
|
||||
|
||||
private void setForced(LcdManager manager, Config config, Command command) throws FreeboxException {
|
||||
if (ON_OFF_CLASSES.contains(command.getClass())) {
|
||||
manager.setOrientationForced(TRUE_COMMANDS.contains(command));
|
||||
} else {
|
||||
logger.warn("Invalid command {} from channel {}", command, LCD_FORCED);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBrightness(LcdManager manager, Config config, Command command) throws FreeboxException {
|
||||
if (command instanceof IncreaseDecreaseType) {
|
||||
manager.setBrightness(() -> config.brightness() + (command == IncreaseDecreaseType.INCREASE ? 1 : -1));
|
||||
} else if (command instanceof OnOffType) {
|
||||
manager.setBrightness(() -> command == OnOffType.ON ? 100 : 0);
|
||||
} else if (command instanceof QuantityType) {
|
||||
manager.setBrightness(() -> ((QuantityType<?>) command).intValue());
|
||||
} else if (command instanceof DecimalType || command instanceof PercentType) {
|
||||
manager.setBrightness(() -> ((DecimalType) command).intValue());
|
||||
} else {
|
||||
logger.warn("Invalid command {} from channel {}", command, LCD_BRIGHTNESS);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
import static org.openhab.core.library.unit.Units.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.action.ServerActions;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.AfpManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.AirMediaManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.ConnectionManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.ConnectionManager.Status;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.FtpManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.Source;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanManager.LanConfig;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.SambaManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.SambaManager.Samba;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.SystemManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.SystemManager.Config;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.UPnPAVManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.WifiManager;
|
||||
import org.openhab.core.library.CoreItemFactory;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ServerHandler} handle common parts of Freebox bridges.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ServerHandler extends ApiConsumerHandler implements FreeDeviceIntf {
|
||||
private static final BigDecimal HUNDRED = BigDecimal.valueOf(100);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ServerHandler.class);
|
||||
private final ChannelUID eventChannelUID;
|
||||
|
||||
private long uptime = -1;
|
||||
|
||||
public ServerHandler(Thing thing) {
|
||||
super(thing);
|
||||
eventChannelUID = new ChannelUID(getThing().getUID(), SYS_INFO, BOX_EVENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initializeProperties(Map<String, String> properties) throws FreeboxException {
|
||||
LanConfig lanConfig = getManager(LanManager.class).getConfig();
|
||||
Config config = getManager(SystemManager.class).getConfig();
|
||||
properties.put(Thing.PROPERTY_SERIAL_NUMBER, config.serial());
|
||||
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, config.firmwareVersion());
|
||||
properties.put(Thing.PROPERTY_HARDWARE_VERSION, config.modelInfo().prettyName());
|
||||
properties.put(Source.UPNP.name(), lanConfig.name());
|
||||
|
||||
List<Channel> channels = new ArrayList<>(getThing().getChannels());
|
||||
config.sensors().forEach(sensor -> {
|
||||
ChannelUID sensorId = new ChannelUID(thing.getUID(), GROUP_SENSORS, sensor.id());
|
||||
channels.add(
|
||||
ChannelBuilder.create(sensorId).withLabel(sensor.name()).withAcceptedItemType("Number:Temperature")
|
||||
.withType(new ChannelTypeUID(BINDING_ID + ":temperature")).build());
|
||||
});
|
||||
config.fans().forEach(sensor -> {
|
||||
ChannelUID sensorId = new ChannelUID(thing.getUID(), GROUP_FANS, sensor.id());
|
||||
channels.add(ChannelBuilder.create(sensorId).withLabel(sensor.name())
|
||||
.withAcceptedItemType(CoreItemFactory.NUMBER).withType(new ChannelTypeUID(BINDING_ID + ":fanspeed"))
|
||||
.build());
|
||||
});
|
||||
updateThing(editThing().withChannels(channels).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
logger.debug("Polling server state...");
|
||||
fetchConnectionStatus();
|
||||
fetchSystemConfig();
|
||||
|
||||
updateChannelOnOff(ACTIONS, WIFI_STATUS, getManager(WifiManager.class).getStatus());
|
||||
updateChannelOnOff(ACTIONS, AIRMEDIA_STATUS, getManager(AirMediaManager.class).getStatus());
|
||||
updateChannelOnOff(ACTIONS, UPNPAV_STATUS, getManager(UPnPAVManager.class).getStatus());
|
||||
|
||||
Samba response = getManager(SambaManager.class).getConfig();
|
||||
updateChannelOnOff(FILE_SHARING, SAMBA_FILE_STATUS, response.fileShareEnabled());
|
||||
updateChannelOnOff(FILE_SHARING, SAMBA_PRINTER_STATUS, response.printShareEnabled());
|
||||
updateChannelOnOff(FILE_SHARING, FTP_STATUS, getManager(FtpManager.class).getStatus());
|
||||
updateChannelOnOff(FILE_SHARING, AFP_FILE_STATUS, getManager(AfpManager.class).getStatus());
|
||||
}
|
||||
|
||||
private void fetchSystemConfig() throws FreeboxException {
|
||||
Config config = getManager(SystemManager.class).getConfig();
|
||||
|
||||
config.sensors().forEach(s -> updateChannelQuantity(GROUP_SENSORS, s.id(), s.value(), SIUnits.CELSIUS));
|
||||
config.fans().forEach(f -> updateChannelQuantity(GROUP_FANS, f.id(), f.value(), Units.RPM));
|
||||
|
||||
uptime = checkUptimeAndFirmware(config.uptimeVal(), uptime, config.firmwareVersion());
|
||||
updateChannelQuantity(SYS_INFO, UPTIME, uptime, Units.SECOND);
|
||||
|
||||
LanConfig lanConfig = getManager(LanManager.class).getConfig();
|
||||
updateChannelString(SYS_INFO, IP_ADDRESS, lanConfig.ip());
|
||||
}
|
||||
|
||||
private void fetchConnectionStatus() throws FreeboxException {
|
||||
Status status = getManager(ConnectionManager.class).getConfig();
|
||||
updateChannelString(CONNECTION_STATUS, LINE_STATUS, status.state());
|
||||
updateChannelString(CONNECTION_STATUS, LINE_TYPE, status.type());
|
||||
updateChannelString(CONNECTION_STATUS, LINE_MEDIA, status.media());
|
||||
updateChannelString(CONNECTION_STATUS, IP_ADDRESS, status.ipv4());
|
||||
updateChannelString(CONNECTION_STATUS, IPV6_ADDRESS, status.ipv6());
|
||||
|
||||
updateRateBandwidth(status.rateUp(), status.bandwidthUp(), "up");
|
||||
updateRateBandwidth(status.rateDown(), status.bandwidthDown(), "down");
|
||||
|
||||
updateChannelQuantity(CONNECTION_STATUS, BYTES_UP, new QuantityType<>(status.bytesUp(), OCTET), GIBIOCTET);
|
||||
updateChannelQuantity(CONNECTION_STATUS, BYTES_DOWN, new QuantityType<>(status.bytesDown(), OCTET), GIBIOCTET);
|
||||
}
|
||||
|
||||
private void updateRateBandwidth(long rate, long bandwidth, String orientation) {
|
||||
QuantityType<?> rateUp = new QuantityType<>(rate * 8, Units.BIT_PER_SECOND);
|
||||
QuantityType<?> bandwidthUp = new QuantityType<>(bandwidth, BIT_PER_SECOND);
|
||||
updateChannelQuantity(CONNECTION_STATUS, RATE + "-" + orientation, rateUp, KILOBIT_PER_SECOND);
|
||||
updateChannelQuantity(CONNECTION_STATUS, BW + "-" + orientation, bandwidthUp, KILOBIT_PER_SECOND);
|
||||
updateChannelQuantity(CONNECTION_STATUS, PCT_BW + "-" + orientation,
|
||||
!bandwidthUp.equals(QuantityType.ZERO) ? rateUp.multiply(HUNDRED).divide(bandwidthUp)
|
||||
: QuantityType.ZERO,
|
||||
Units.PERCENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean internalHandleCommand(String channelId, Command command) throws FreeboxException {
|
||||
if (ON_OFF_CLASSES.contains(command.getClass())) {
|
||||
boolean enable = TRUE_COMMANDS.contains(command);
|
||||
switch (channelId) {
|
||||
case WIFI_STATUS:
|
||||
updateChannelOnOff(ACTIONS, WIFI_STATUS, getManager(WifiManager.class).setStatus(enable));
|
||||
return true;
|
||||
case FTP_STATUS:
|
||||
updateChannelOnOff(FILE_SHARING, FTP_STATUS, getManager(FtpManager.class).setStatus(enable));
|
||||
return true;
|
||||
case SAMBA_FILE_STATUS:
|
||||
updateChannelOnOff(FILE_SHARING, SAMBA_FILE_STATUS,
|
||||
getManager(SambaManager.class).setFileShare(enable));
|
||||
return true;
|
||||
case SAMBA_PRINTER_STATUS:
|
||||
updateChannelOnOff(FILE_SHARING, SAMBA_PRINTER_STATUS,
|
||||
getManager(SambaManager.class).setPrintShare(enable));
|
||||
return true;
|
||||
case UPNPAV_STATUS:
|
||||
updateChannelOnOff(ACTIONS, UPNPAV_STATUS, getManager(UPnPAVManager.class).setStatus(enable));
|
||||
return true;
|
||||
case AFP_FILE_STATUS:
|
||||
updateChannelOnOff(FILE_SHARING, AFP_FILE_STATUS, getManager(AfpManager.class).setStatus(enable));
|
||||
return true;
|
||||
case AIRMEDIA_STATUS:
|
||||
updateChannelOnOff(ACTIONS, AIRMEDIA_STATUS, getManager(AirMediaManager.class).setStatus(enable));
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return super.internalHandleCommand(channelId, command);
|
||||
}
|
||||
|
||||
public void reboot() {
|
||||
processReboot(() -> {
|
||||
try {
|
||||
getManager(SystemManager.class).reboot();
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error rebooting: {}", e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(ServerActions.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelUID getEventChannelUID() {
|
||||
return eventChannelUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerChannel(ChannelUID channelUID, String event) {
|
||||
super.triggerChannel(channelUID, event);
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.Endpoint;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.EndpointState;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StopMoveType;
|
||||
import org.openhab.core.library.types.UpDownType;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* The {@link ShutterHandler} is responsible for handling everything associated to any Freebox Home shutter.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ShutterHandler extends HomeNodeHandler {
|
||||
|
||||
public ShutterHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalConfigureChannel(String channelId, Configuration conf, List<Endpoint> endpoints) {
|
||||
endpoints.stream().filter(ep -> channelId.equals(SHUTTER_POSITION) && ep.name().equals(SHUTTER_STOP))
|
||||
.forEach(endPoint -> conf.put(endPoint.name(), endPoint.id()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State getChannelState(HomeManager homeManager, String channelId, EndpointState state) {
|
||||
String value = state.value();
|
||||
return value != null && channelId.equals(SHUTTER_POSITION) ? QuantityType.valueOf(value + " %")
|
||||
: UnDefType.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean executeSlotCommand(HomeManager homeManager, String channelId, Command command,
|
||||
Configuration config, int positionSlot) throws FreeboxException {
|
||||
Integer stopSlot = getSlotId(config, SHUTTER_STOP);
|
||||
if (SHUTTER_POSITION.equals(channelId) && stopSlot instanceof Integer) {
|
||||
if (command instanceof UpDownType upDownCmd) {
|
||||
return operateShutter(homeManager, stopSlot, positionSlot, upDownCmd == UpDownType.DOWN ? 100 : 0);
|
||||
} else if (command instanceof StopMoveType stopMove && stopMove == StopMoveType.STOP) {
|
||||
return operateShutter(homeManager, stopSlot, positionSlot, -1);
|
||||
} else if (command instanceof Number numberCmd) {
|
||||
return operateShutter(homeManager, stopSlot, positionSlot, numberCmd.intValue());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean operateShutter(HomeManager homeManager, int stopSlot, int positionSlot, int target)
|
||||
throws FreeboxException {
|
||||
homeManager.putCommand(getClientId(), stopSlot, true);
|
||||
if (target >= 0) {
|
||||
homeManager.putCommand(getClientId(), positionSlot, target);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.VmManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.VmManager.Status;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.VmManager.VirtualMachine;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.WebSocketManager;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link VmHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class VmHandler extends HostHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(VmHandler.class);
|
||||
|
||||
// We start in pull mode and switch to push after a first update
|
||||
private boolean pushSubscribed = false;
|
||||
|
||||
public VmHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
try {
|
||||
getManager(WebSocketManager.class).unregisterVm(getClientId());
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error unregistering VM from the websocket: {}", e.getMessage());
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
if (pushSubscribed) {
|
||||
return;
|
||||
}
|
||||
super.internalPoll();
|
||||
|
||||
logger.debug("Polling Virtual machine status");
|
||||
VirtualMachine vm = getManager(VmManager.class).getDevice(getClientId());
|
||||
updateVmChannels(vm);
|
||||
getManager(WebSocketManager.class).registerVm(vm.id(), this);
|
||||
pushSubscribed = true;
|
||||
}
|
||||
|
||||
public void updateVmChannels(VirtualMachine vm) {
|
||||
boolean running = Status.RUNNING.equals(vm.status());
|
||||
updateChannelOnOff(VM_STATUS, STATUS, running);
|
||||
updateChannelOnOff(CONNECTIVITY, REACHABLE, running);
|
||||
updateStatus(running ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean internalHandleCommand(String channelId, Command command) throws FreeboxException {
|
||||
if (STATUS.equals(channelId) && command instanceof OnOffType) {
|
||||
getManager(VmManager.class).power(getClientId(), OnOffType.ON.equals(command));
|
||||
return true;
|
||||
}
|
||||
return super.internalHandleCommand(channelId, command);
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.handler;
|
||||
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.APManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.APManager.LanAccessPoint;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.APManager.Station;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.RepeaterManager;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* The {@link WifiStationHandler} is responsible for handling everything associated to
|
||||
* any Freebox thing types except the bridge thing type.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WifiStationHandler extends HostHandler {
|
||||
private static final String SERVER_HOST = "Server";
|
||||
|
||||
public WifiStationHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
super.internalPoll();
|
||||
|
||||
// Search if the wifi-host is hosted on server access-points
|
||||
Optional<Station> station = getManager(APManager.class).getStation(getMac());
|
||||
if (station.isPresent()) {
|
||||
Station data = station.get();
|
||||
updateChannelDateTimeState(CONNECTIVITY, LAST_SEEN, data.getLastSeen());
|
||||
updateChannelString(GROUP_WIFI, WIFI_HOST, SERVER_HOST);
|
||||
updateWifiStationChannels(data.signal(), data.getSsid(), data.rxRate(), data.txRate());
|
||||
return;
|
||||
}
|
||||
|
||||
// Search if it is hosted by a repeater
|
||||
Optional<LanHost> wifiHost = getManager(RepeaterManager.class).getHost(getMac());
|
||||
if (wifiHost.isPresent()) {
|
||||
updateChannelDateTimeState(CONNECTIVITY, LAST_SEEN, wifiHost.get().getLastSeen());
|
||||
LanAccessPoint lanAp = wifiHost.get().accessPoint();
|
||||
if (lanAp != null) {
|
||||
updateChannelString(GROUP_WIFI, WIFI_HOST, "%s-%s".formatted(lanAp.type(), lanAp.uid()));
|
||||
updateWifiStationChannels(lanAp.getSignal(), lanAp.getSsid(), lanAp.rxRate(), lanAp.txRate());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Not found a wifi repeater/host, so update all wifi channels to NULL
|
||||
getThing().getChannelsOfGroup(GROUP_WIFI).stream().map(Channel::getUID).filter(uid -> isLinked(uid))
|
||||
.forEach(uid -> updateState(uid, UnDefType.NULL));
|
||||
}
|
||||
|
||||
private void updateWifiStationChannels(int rssi, @Nullable String ssid, long rxRate, long txRate) {
|
||||
updateChannelString(GROUP_WIFI, SSID, ssid);
|
||||
updateChannelQuantity(GROUP_WIFI, RSSI, rssi <= 0 ? new QuantityType<>(rssi, Units.DECIBEL_MILLIWATTS) : null);
|
||||
updateChannelDecimal(GROUP_WIFI, WIFI_QUALITY, rssi <= 0 ? toQoS(rssi) : null);
|
||||
updateRateChannel(RATE + "-down", rxRate);
|
||||
updateRateChannel(RATE + "-up", txRate);
|
||||
}
|
||||
|
||||
private void updateRateChannel(String channel, long rate) {
|
||||
QuantityType<?> qtty = rate != -1 ? new QuantityType<>(rate * 8, Units.BIT_PER_SECOND) : null;
|
||||
updateChannelQuantity(GROUP_WIFI, channel, qtty);
|
||||
}
|
||||
|
||||
private int toQoS(int rssi) {
|
||||
return rssi > -50 ? 4 : rssi > -60 ? 3 : rssi > -70 ? 2 : rssi > -85 ? 1 : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<addon:addon id="freeboxos" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||
|
||||
<type>binding</type>
|
||||
<name>Freebox OS Binding</name>
|
||||
<description>The Freebox OS binding integrates Free equipments in your home automation.</description>
|
||||
<connection>local</connection>
|
||||
<countries>fr,it</countries>
|
||||
|
||||
<config-description>
|
||||
<parameter name="timeout" type="integer" required="false" min="1" unit="s">
|
||||
<label>Timeout</label>
|
||||
<description>The timeout for reading from the API in seconds.</description>
|
||||
<default>8</default>
|
||||
</parameter>
|
||||
<parameter name="callbackUrl" type="text" required="false">
|
||||
<label>Callback URL</label>
|
||||
<description>URL to use for playing notification sounds hosted by openHAB, e.g. 'http://192.168.0.2:8080'</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</addon:addon>
|
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="bridge-type:freeboxos:api">
|
||||
<parameter name="apiDomain" type="text">
|
||||
<label>Freebox Server Address</label>
|
||||
<context>network-address</context>
|
||||
<description>The domain to use in place of hardcoded Freebox ip</description>
|
||||
<default>mafreebox.freebox.fr</default>
|
||||
</parameter>
|
||||
<parameter name="appToken" type="text" required="false">
|
||||
<label>Application Token</label>
|
||||
<context>password</context>
|
||||
<description>Token generated by the Freebox server</description>
|
||||
</parameter>
|
||||
<parameter name="discoverNetDevice" type="boolean">
|
||||
<label>Network Device Discovery</label>
|
||||
<description>Enable the discovery of network device things</description>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
<parameter name="httpsAvailable" type="boolean">
|
||||
<label>HTTPS Available</label>
|
||||
<description>Tells if https has been configured on the Freebox</description>
|
||||
<advanced>true</advanced>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
<parameter name="httpsPort" type="integer">
|
||||
<label>HTTPS port</label>
|
||||
<description>Port to use for remote https access to the Freebox Api</description>
|
||||
<advanced>true</advanced>
|
||||
<default>15682</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:freeboxos:home-node">
|
||||
<parameter name="id" type="integer" required="true">
|
||||
<label>ID</label>
|
||||
<description>Id of the Home Node</description>
|
||||
<default>1</default>
|
||||
</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 the Node</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:freeboxos:host">
|
||||
<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 device</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="macAddress" type="text" pattern="([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})" required="true">
|
||||
<label>MAC Address</label>
|
||||
<description>The MAC address of the network device</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:freeboxos:phone">
|
||||
<parameter name="refreshInterval" type="integer" min="1" unit="s">
|
||||
<label>State Refresh Interval</label>
|
||||
<description>The refresh interval in seconds which is used to poll for phone state.</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="id" type="integer">
|
||||
<label>ID</label>
|
||||
<description>Id of the phone line</description>
|
||||
<default>1</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="thing-type:freeboxos:call">
|
||||
<parameter name="refreshInterval" type="integer" min="1" unit="s">
|
||||
<label>State Refresh Interval</label>
|
||||
<description>The refresh interval in seconds which is used to poll for phone state.</description>
|
||||
<default>2</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:freeboxos:player">
|
||||
<parameter name="macAddress" type="text" required="true" pattern="([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})">
|
||||
<label>MAC Address</label>
|
||||
<description>The MAC address of the player device</description>
|
||||
</parameter>
|
||||
<parameter name="id" type="integer">
|
||||
<label>ID</label>
|
||||
<description>Id of the player</description>
|
||||
<default>1</default>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer">
|
||||
<label>Player port</label>
|
||||
<default>24322</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="password" type="text" required="false">
|
||||
<context>password</context>
|
||||
<label>Password</label>
|
||||
<description>AirPlay password</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="remoteCode" type="text" required="false">
|
||||
<label>Remote Code</label>
|
||||
<description>Code associated to remote control</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="acceptAllMp3" type="boolean" required="false">
|
||||
<label>Accept All MP3</label>
|
||||
<description>Accept any bitrate for MP3 audio or only bitrates greater than 64 kbps</description>
|
||||
<default>true</default>
|
||||
<advanced>true</advanced>
|
||||
</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 the player</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:freeboxos:repeater">
|
||||
<parameter name="refreshInterval" type="integer" min="1" unit="s">
|
||||
<label>Refresh Interval</label>
|
||||
<description>The refresh interval in seconds which is used to poll the repeater</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="macAddress" type="text" pattern="([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})" required="true">
|
||||
<label>MAC Address</label>
|
||||
<description>The MAC address of the network device</description>
|
||||
</parameter>
|
||||
<parameter name="id" type="integer" required="true">
|
||||
<label>ID</label>
|
||||
<description>Id of the repeater</description>
|
||||
<default>1</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:freeboxos:server">
|
||||
<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>
|
||||
</parameter>
|
||||
<parameter name="macAddress" type="text" pattern="([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})" required="true">
|
||||
<label>MAC Address</label>
|
||||
<description>The MAC address of the network device</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:freeboxos:vm">
|
||||
<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 virtual machine</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="macAddress" type="text" pattern="([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})" required="true">
|
||||
<label>MAC Address</label>
|
||||
<description>The MAC address of the network device</description>
|
||||
</parameter>
|
||||
<parameter name="id" type="integer" required="true">
|
||||
<label>ID</label>
|
||||
<description>Id of the Virtual Machine</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,354 @@
|
||||
# add-on
|
||||
|
||||
addon.freeboxos.name = Freebox OS Binding
|
||||
addon.freeboxos.description = The Freebox OS binding integrates Free equipments in your home automation.
|
||||
|
||||
# add-on config
|
||||
|
||||
addon.config.freeboxos.callbackUrl.label = Callback URL
|
||||
addon.config.freeboxos.callbackUrl.description = URL to use for playing notification sounds hosted by openHAB, e.g. 'http://192.168.0.2:8080'
|
||||
addon.config.freeboxos.timeout.label = Timeout
|
||||
addon.config.freeboxos.timeout.description = The timeout for reading from the API in seconds.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.freeboxos.active-player.label = Freebox Player
|
||||
thing-type.freeboxos.active-player.description = The player is the device connected to your TV with API capabilities
|
||||
thing-type.freeboxos.alarm.label = Freebox Alarm
|
||||
thing-type.freeboxos.alarm.description = The Alarm system configured in your Freebox Delta Server
|
||||
thing-type.freeboxos.alarm.channel.sound.label = Bips Volume
|
||||
thing-type.freeboxos.alarm.channel.timeout1.label = Alarm Activation Duration
|
||||
thing-type.freeboxos.alarm.channel.timeout2.label = Safe Zone Alert Timeout
|
||||
thing-type.freeboxos.alarm.channel.timeout3.label = Alert Duration
|
||||
thing-type.freeboxos.alarm.channel.volume.label = Alarm Volume
|
||||
thing-type.freeboxos.api.label = Freebox OS Api
|
||||
thing-type.freeboxos.api.description = Bridge between hosts and the API rest service
|
||||
thing-type.freeboxos.basic-shutter.label = Freebox Home Basic Shutter
|
||||
thing-type.freeboxos.basic-shutter.description = The Basic Shutter (UP,DOWN,STOP) configured in your Freebox Delta Server
|
||||
thing-type.freeboxos.call.label = Calls
|
||||
thing-type.freeboxos.call.description = Provides various informations regarding the phone calls
|
||||
thing-type.freeboxos.dect.label = DECT
|
||||
thing-type.freeboxos.dect.description = Provides various informations regarding the DECT state and configuration
|
||||
thing-type.freeboxos.dect.channel.gain-rx.label = Gain RX
|
||||
thing-type.freeboxos.dect.channel.gain-tx.label = Gain TX
|
||||
thing-type.freeboxos.delta.label = Freebox Delta
|
||||
thing-type.freeboxos.delta.description = Provides various informations regarding the status of the Freebox Delta Server
|
||||
thing-type.freeboxos.freeplug.label = Freeplug
|
||||
thing-type.freeboxos.freeplug.description = Ethernet / CPL gateway
|
||||
thing-type.freeboxos.freeplug.channel.last-seen.label = Last Activity
|
||||
thing-type.freeboxos.freeplug.channel.rate-down.label = Rx Rate
|
||||
thing-type.freeboxos.freeplug.channel.rate-down.description = Current RX rate
|
||||
thing-type.freeboxos.freeplug.channel.rate-up.label = Tx Rate
|
||||
thing-type.freeboxos.freeplug.channel.rate-up.description = Current TX Rate
|
||||
thing-type.freeboxos.fxs.label = Landline
|
||||
thing-type.freeboxos.fxs.description = Provides various informations regarding the landline state
|
||||
thing-type.freeboxos.host.label = Network Device
|
||||
thing-type.freeboxos.host.description = Provides network device reachability
|
||||
thing-type.freeboxos.kfb.label = Freebox Keyfob
|
||||
thing-type.freeboxos.kfb.description = A keyfob configured for your Freebox Security system
|
||||
thing-type.freeboxos.player.label = Freebox Player
|
||||
thing-type.freeboxos.player.description = The player is the device connected to your TV
|
||||
thing-type.freeboxos.repeater.label = Wifi Repeater
|
||||
thing-type.freeboxos.repeater.description = Provides informations and control over a Wifi Repeater
|
||||
thing-type.freeboxos.revolution.label = Freebox Revolution
|
||||
thing-type.freeboxos.revolution.description = Provides various informations regarding the status of the Freebox Revolution Server
|
||||
thing-type.freeboxos.shutter.label = Freebox Home Shutter
|
||||
thing-type.freeboxos.shutter.description = The Shutter configured in your Freebox Delta Server
|
||||
thing-type.freeboxos.vm.label = Virtual Machine
|
||||
thing-type.freeboxos.vm.description = Provides informations and control over virtual machine hosted on the server
|
||||
thing-type.freeboxos.wifihost.label = Wifi Device
|
||||
thing-type.freeboxos.wifihost.description = Provides Wifi device reachability
|
||||
|
||||
# thing types config
|
||||
|
||||
bridge-type.config.freeboxos.api.apiDomain.label = Freebox Server Address
|
||||
bridge-type.config.freeboxos.api.apiDomain.description = The domain to use in place of hardcoded Freebox ip
|
||||
bridge-type.config.freeboxos.api.appToken.label = Application Token
|
||||
bridge-type.config.freeboxos.api.appToken.description = Token generated by the Freebox server
|
||||
bridge-type.config.freeboxos.api.discoverNetDevice.label = Network Device Discovery
|
||||
bridge-type.config.freeboxos.api.discoverNetDevice.description = Enable the discovery of network device things
|
||||
bridge-type.config.freeboxos.api.httpsAvailable.label = HTTPS Available
|
||||
bridge-type.config.freeboxos.api.httpsAvailable.description = Tells if https has been configured on the Freebox
|
||||
bridge-type.config.freeboxos.api.httpsPort.label = HTTPS port
|
||||
bridge-type.config.freeboxos.api.httpsPort.description = Port to use for remote https access to the Freebox Api
|
||||
thing-type.config.freeboxos.call.refreshInterval.label = State Refresh Interval
|
||||
thing-type.config.freeboxos.call.refreshInterval.description = The refresh interval in seconds which is used to poll for phone state.
|
||||
thing-type.config.freeboxos.home-node.id.label = ID
|
||||
thing-type.config.freeboxos.home-node.id.description = Id of the Home Node
|
||||
thing-type.config.freeboxos.home-node.refreshInterval.label = Refresh Interval
|
||||
thing-type.config.freeboxos.home-node.refreshInterval.description = The refresh interval in seconds which is used to poll the Node
|
||||
thing-type.config.freeboxos.host.macAddress.label = MAC Address
|
||||
thing-type.config.freeboxos.host.macAddress.description = The MAC address of the network device
|
||||
thing-type.config.freeboxos.host.refreshInterval.label = Refresh Interval
|
||||
thing-type.config.freeboxos.host.refreshInterval.description = The refresh interval in seconds which is used to poll given device
|
||||
thing-type.config.freeboxos.phone.id.label = ID
|
||||
thing-type.config.freeboxos.phone.id.description = Id of the phone line
|
||||
thing-type.config.freeboxos.phone.refreshInterval.label = State Refresh Interval
|
||||
thing-type.config.freeboxos.phone.refreshInterval.description = The refresh interval in seconds which is used to poll for phone state.
|
||||
thing-type.config.freeboxos.player.acceptAllMp3.label = Accept All MP3
|
||||
thing-type.config.freeboxos.player.acceptAllMp3.description = Accept any bitrate for MP3 audio or only bitrates greater than 64 kbps
|
||||
thing-type.config.freeboxos.player.id.label = ID
|
||||
thing-type.config.freeboxos.player.id.description = Id of the player
|
||||
thing-type.config.freeboxos.player.macAddress.label = MAC Address
|
||||
thing-type.config.freeboxos.player.macAddress.description = The MAC address of the player device
|
||||
thing-type.config.freeboxos.player.password.label = Password
|
||||
thing-type.config.freeboxos.player.password.description = AirPlay password
|
||||
thing-type.config.freeboxos.player.port.label = Player port
|
||||
thing-type.config.freeboxos.player.refreshInterval.label = Refresh Interval
|
||||
thing-type.config.freeboxos.player.refreshInterval.description = The refresh interval in seconds which is used to poll the player
|
||||
thing-type.config.freeboxos.player.remoteCode.label = Remote Code
|
||||
thing-type.config.freeboxos.player.remoteCode.description = Code associated to remote control
|
||||
thing-type.config.freeboxos.repeater.id.label = ID
|
||||
thing-type.config.freeboxos.repeater.id.description = Id of the repeater
|
||||
thing-type.config.freeboxos.repeater.macAddress.label = MAC Address
|
||||
thing-type.config.freeboxos.repeater.macAddress.description = The MAC address of the network device
|
||||
thing-type.config.freeboxos.repeater.refreshInterval.label = Refresh Interval
|
||||
thing-type.config.freeboxos.repeater.refreshInterval.description = The refresh interval in seconds which is used to poll the repeater
|
||||
thing-type.config.freeboxos.server.macAddress.label = MAC Address
|
||||
thing-type.config.freeboxos.server.macAddress.description = The MAC address of the network device
|
||||
thing-type.config.freeboxos.server.refreshInterval.label = Refresh Interval
|
||||
thing-type.config.freeboxos.server.refreshInterval.description = The refresh interval in seconds which is used to poll given Freebox Server
|
||||
thing-type.config.freeboxos.vm.id.label = ID
|
||||
thing-type.config.freeboxos.vm.id.description = Id of the Virtual Machine
|
||||
thing-type.config.freeboxos.vm.macAddress.label = MAC Address
|
||||
thing-type.config.freeboxos.vm.macAddress.description = The MAC address of the network device
|
||||
thing-type.config.freeboxos.vm.refreshInterval.label = Refresh Interval
|
||||
thing-type.config.freeboxos.vm.refreshInterval.description = The refresh interval in seconds which is used to poll given virtual machine
|
||||
|
||||
# channel group types
|
||||
|
||||
channel-group-type.freeboxos.accepted.label = Accepted Call
|
||||
channel-group-type.freeboxos.accepted.description = The last accepted phone call
|
||||
channel-group-type.freeboxos.accepted.channel.duration.label = Incoming Call Duration
|
||||
channel-group-type.freeboxos.accepted.channel.name.label = Accepted Caller
|
||||
channel-group-type.freeboxos.accepted.channel.name.description = Caller name
|
||||
channel-group-type.freeboxos.accepted.channel.number.label = Calling Number
|
||||
channel-group-type.freeboxos.accepted.channel.number.description = Caller phone number
|
||||
channel-group-type.freeboxos.accepted.channel.timestamp.label = Incoming Call Timestamp
|
||||
channel-group-type.freeboxos.actions.label = Server Settings
|
||||
channel-group-type.freeboxos.connection-status.label = Connection Status Details
|
||||
channel-group-type.freeboxos.connection-status.channel.bandwidth-down.label = Bandwidth Down
|
||||
channel-group-type.freeboxos.connection-status.channel.bandwidth-down.description = Raw value of the download bandwidth currently used
|
||||
channel-group-type.freeboxos.connection-status.channel.bandwidth-up.label = Bandwidth Up
|
||||
channel-group-type.freeboxos.connection-status.channel.bandwidth-up.description = Raw value of the upload bandwidth currently used
|
||||
channel-group-type.freeboxos.connection-status.channel.bandwidth-usage-down.label = Bandwidth Usage Down
|
||||
channel-group-type.freeboxos.connection-status.channel.bandwidth-usage-down.description = Portion of the download bandwidth currently used
|
||||
channel-group-type.freeboxos.connection-status.channel.bandwidth-usage-up.label = Bandwidth Usage Up
|
||||
channel-group-type.freeboxos.connection-status.channel.bandwidth-usage-up.description = Portion of the upload bandwidth currently used
|
||||
channel-group-type.freeboxos.connection-status.channel.bytes-down.label = Downloaded
|
||||
channel-group-type.freeboxos.connection-status.channel.bytes-down.description = Total data downloaded since last restart
|
||||
channel-group-type.freeboxos.connection-status.channel.bytes-up.label = Uploaded
|
||||
channel-group-type.freeboxos.connection-status.channel.bytes-up.description = Total data uploaded since last restart
|
||||
channel-group-type.freeboxos.connection-status.channel.ip-address.label = Public IPv4
|
||||
channel-group-type.freeboxos.connection-status.channel.ip-address.description = Public IPv4 Address of the Freebox (this field is only available when connection state is up)
|
||||
channel-group-type.freeboxos.connection-status.channel.ipv6-address.label = Public IPv6
|
||||
channel-group-type.freeboxos.connection-status.channel.ipv6-address.description = Public IPv6 Address of the Freebox (this field is only available when connection state is up)
|
||||
channel-group-type.freeboxos.connection-status.channel.rate-down.label = Download Rate
|
||||
channel-group-type.freeboxos.connection-status.channel.rate-down.description = Current download rate
|
||||
channel-group-type.freeboxos.connection-status.channel.rate-up.label = Upload Rate
|
||||
channel-group-type.freeboxos.connection-status.channel.rate-up.description = Current upload rate
|
||||
channel-group-type.freeboxos.connectivity.label = Network Connectivity
|
||||
channel-group-type.freeboxos.connectivity.channel.ip-address.label = IP Address
|
||||
channel-group-type.freeboxos.connectivity.channel.ip-address.description = IPv4 Address of the host
|
||||
channel-group-type.freeboxos.connectivity.channel.last-seen.label = Last Activity
|
||||
channel-group-type.freeboxos.display.label = Front Display Panel
|
||||
channel-group-type.freeboxos.fans.label = Fans
|
||||
channel-group-type.freeboxos.file-sharing.label = File Sharing
|
||||
channel-group-type.freeboxos.incoming.label = Incoming Call
|
||||
channel-group-type.freeboxos.incoming.description = Currently presented phone call
|
||||
channel-group-type.freeboxos.incoming.channel.name.label = Incoming Caller
|
||||
channel-group-type.freeboxos.incoming.channel.name.description = Caller name
|
||||
channel-group-type.freeboxos.incoming.channel.number.label = Calling Number
|
||||
channel-group-type.freeboxos.incoming.channel.number.description = Caller phone number
|
||||
channel-group-type.freeboxos.incoming.channel.timestamp.label = Call Timestamp
|
||||
channel-group-type.freeboxos.missed.label = Missed Call
|
||||
channel-group-type.freeboxos.missed.description = The last missed phone call
|
||||
channel-group-type.freeboxos.missed.channel.name.label = Missed Caller
|
||||
channel-group-type.freeboxos.missed.channel.name.description = Caller name
|
||||
channel-group-type.freeboxos.missed.channel.number.label = Missed Calling Number
|
||||
channel-group-type.freeboxos.missed.channel.number.description = Caller phone number
|
||||
channel-group-type.freeboxos.missed.channel.timestamp.label = Missed Call Timestamp
|
||||
channel-group-type.freeboxos.outgoing.label = Outgoing Call
|
||||
channel-group-type.freeboxos.outgoing.description = The last outgoing phone call
|
||||
channel-group-type.freeboxos.outgoing.channel.duration.label = Outgoing Call Duration
|
||||
channel-group-type.freeboxos.outgoing.channel.name.label = Called Name
|
||||
channel-group-type.freeboxos.outgoing.channel.name.description = Name, if known, of the called person
|
||||
channel-group-type.freeboxos.outgoing.channel.number.label = Called Number
|
||||
channel-group-type.freeboxos.outgoing.channel.number.description = Called phone number
|
||||
channel-group-type.freeboxos.outgoing.channel.timestamp.label = Outgoing Call Timestamp
|
||||
channel-group-type.freeboxos.player-actions.label = Player Actions
|
||||
channel-group-type.freeboxos.player-status.label = Player Status
|
||||
channel-group-type.freeboxos.player-sysinfo.label = System Informations
|
||||
channel-group-type.freeboxos.repeater-misc.label = Repeater Settings
|
||||
channel-group-type.freeboxos.repeater-misc.channel.box-event.label = Repeater Event
|
||||
channel-group-type.freeboxos.sensors.label = System Sensors
|
||||
channel-group-type.freeboxos.sysinfo.label = System Informations
|
||||
channel-group-type.freeboxos.sysinfo.channel.ip-address.label = Internal IP
|
||||
channel-group-type.freeboxos.sysinfo.channel.ip-address.description = Internal IPv4 Address of the host
|
||||
channel-group-type.freeboxos.vmstatus.label = VM Status
|
||||
channel-group-type.freeboxos.wifi.label = Wifi Related Information
|
||||
channel-group-type.freeboxos.wifi.channel.rate-down.label = Rx Rate
|
||||
channel-group-type.freeboxos.wifi.channel.rate-down.description = Current RX rate
|
||||
channel-group-type.freeboxos.wifi.channel.rate-up.label = Tx Rate
|
||||
channel-group-type.freeboxos.wifi.channel.rate-up.description = Current TX Rate
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.freeboxos.afp-file-status.label = Mac OS File Sharing
|
||||
channel-type.freeboxos.afp-file-status.description = Status of Mac OS File Sharing
|
||||
channel-type.freeboxos.airmedia-status.label = Air Media Enabled
|
||||
channel-type.freeboxos.airmedia-status.description = Indicates whether Air Media is enabled
|
||||
channel-type.freeboxos.alarm-pin.label = PIN Code
|
||||
channel-type.freeboxos.alarm-timeout.label = Alarm Duration
|
||||
channel-type.freeboxos.alarm-volume.label = Alarm Volume
|
||||
channel-type.freeboxos.alternate-ring.label = Alternating Ring
|
||||
channel-type.freeboxos.bandwidth-usage.label = Bandwidth Usage
|
||||
channel-type.freeboxos.bandwidth-usage.description = Current bandwidth usage
|
||||
channel-type.freeboxos.bandwidth.label = Bandwidth
|
||||
channel-type.freeboxos.bandwidth.description = Available bandwidth
|
||||
channel-type.freeboxos.basic-shutter.label = Shutter
|
||||
channel-type.freeboxos.basic-shutter.description = Shutter command
|
||||
channel-type.freeboxos.box-event.label = Server Event
|
||||
channel-type.freeboxos.box-event.description = Triggers when an event related to the Freebox server has been detected
|
||||
channel-type.freeboxos.connection-status.label = Connection
|
||||
channel-type.freeboxos.connection-status.description = Connection Status
|
||||
channel-type.freeboxos.dect-active.label = DECT Enabled
|
||||
channel-type.freeboxos.dect-active.description = Activates / stops the integrated DECT base
|
||||
channel-type.freeboxos.duration.label = Duration
|
||||
channel-type.freeboxos.duration.description = Call duration in seconds
|
||||
channel-type.freeboxos.fanspeed.label = Fan Speed
|
||||
channel-type.freeboxos.fanspeed.description = Actual measured rotation speed of the fan
|
||||
channel-type.freeboxos.ftp-status.label = FTP Server Enabled
|
||||
channel-type.freeboxos.ftp-status.description = Indicates whether the FTP server is enabled
|
||||
channel-type.freeboxos.gain.label = Gain
|
||||
channel-type.freeboxos.hardware-status.label = Hardware Status
|
||||
channel-type.freeboxos.hardware-status.description = Hardware status of the phone line
|
||||
channel-type.freeboxos.host-count.label = Host Count
|
||||
channel-type.freeboxos.host-count.description = Number of hosts connected to the device
|
||||
channel-type.freeboxos.ip-address.label = IP Address
|
||||
channel-type.freeboxos.ip-address.description = IP address of the client
|
||||
channel-type.freeboxos.key-code.label = Remote Key Code
|
||||
channel-type.freeboxos.key-code.description = Simulates pushing a remote control button
|
||||
channel-type.freeboxos.key-code.state.option.red = Red
|
||||
channel-type.freeboxos.key-code.state.option.green = Green
|
||||
channel-type.freeboxos.key-code.state.option.blue = Blue
|
||||
channel-type.freeboxos.key-code.state.option.yellow = Yellow
|
||||
channel-type.freeboxos.key-code.state.option.power = On/Off
|
||||
channel-type.freeboxos.key-code.state.option.list = List
|
||||
channel-type.freeboxos.key-code.state.option.tv = TV
|
||||
channel-type.freeboxos.key-code.state.option.0 = 0
|
||||
channel-type.freeboxos.key-code.state.option.1 = 1
|
||||
channel-type.freeboxos.key-code.state.option.2 = 2
|
||||
channel-type.freeboxos.key-code.state.option.3 = 3
|
||||
channel-type.freeboxos.key-code.state.option.4 = 4
|
||||
channel-type.freeboxos.key-code.state.option.5 = 5
|
||||
channel-type.freeboxos.key-code.state.option.6 = 6
|
||||
channel-type.freeboxos.key-code.state.option.7 = 7
|
||||
channel-type.freeboxos.key-code.state.option.8 = 8
|
||||
channel-type.freeboxos.key-code.state.option.9 = 9
|
||||
channel-type.freeboxos.key-code.state.option.vol_inc = Volume Up
|
||||
channel-type.freeboxos.key-code.state.option.vol_dec = Volume Down
|
||||
channel-type.freeboxos.key-code.state.option.mute = Mute
|
||||
channel-type.freeboxos.key-code.state.option.prgm_inc = Prog Up
|
||||
channel-type.freeboxos.key-code.state.option.prgm_dec = Prog Down
|
||||
channel-type.freeboxos.key-code.state.option.prev = Previous
|
||||
channel-type.freeboxos.key-code.state.option.bwd = Backward
|
||||
channel-type.freeboxos.key-code.state.option.play = Play/Pause
|
||||
channel-type.freeboxos.key-code.state.option.rec = Record
|
||||
channel-type.freeboxos.key-code.state.option.fwd = Forward
|
||||
channel-type.freeboxos.key-code.state.option.next = Next
|
||||
channel-type.freeboxos.key-code.state.option.up = Up
|
||||
channel-type.freeboxos.key-code.state.option.right = Right
|
||||
channel-type.freeboxos.key-code.state.option.down = Down
|
||||
channel-type.freeboxos.key-code.state.option.left = Left
|
||||
channel-type.freeboxos.key-code.state.option.back = Back
|
||||
channel-type.freeboxos.key-code.state.option.swap = Swap
|
||||
channel-type.freeboxos.key-code.state.option.info = Info
|
||||
channel-type.freeboxos.key-code.state.option.epg = EPG
|
||||
channel-type.freeboxos.key-code.state.option.mail = Mail
|
||||
channel-type.freeboxos.key-code.state.option.media = Media
|
||||
channel-type.freeboxos.key-code.state.option.help = Help
|
||||
channel-type.freeboxos.key-code.state.option.options = Options
|
||||
channel-type.freeboxos.key-code.state.option.pip = PIP
|
||||
channel-type.freeboxos.key-code.state.option.ok = OK
|
||||
channel-type.freeboxos.key-code.state.option.home = Home
|
||||
channel-type.freeboxos.keyfob-enable.label = Keyfob Enabled
|
||||
channel-type.freeboxos.keyfob-enable.description = Activates / deactivates the keyfob
|
||||
channel-type.freeboxos.lcd-brightness.label = Screen Brightness
|
||||
channel-type.freeboxos.lcd-brightness.description = Brightness level of the screen in percent
|
||||
channel-type.freeboxos.lcd-forced.label = Forced Orientation
|
||||
channel-type.freeboxos.lcd-forced.description = Indicates whether the screen orientation is forced
|
||||
channel-type.freeboxos.lcd-orientation.label = Screen Orientation
|
||||
channel-type.freeboxos.lcd-orientation.description = Screen Orientation in degrees
|
||||
channel-type.freeboxos.lcd-orientation.state.option.0 = Horizontal
|
||||
channel-type.freeboxos.lcd-orientation.state.option.90 = Turned left
|
||||
channel-type.freeboxos.lcd-orientation.state.option.180 = Reversed
|
||||
channel-type.freeboxos.lcd-orientation.state.option.270 = Turned right
|
||||
channel-type.freeboxos.led.label = Led Activated
|
||||
channel-type.freeboxos.led.description = Led indicator status
|
||||
channel-type.freeboxos.line-media.label = Line Media
|
||||
channel-type.freeboxos.line-media.description = Media of network line connection
|
||||
channel-type.freeboxos.line-media.state.option.FTTH = FTTH
|
||||
channel-type.freeboxos.line-media.state.option.ETHERNET = Ethernet
|
||||
channel-type.freeboxos.line-media.state.option.XDSL = xDSL
|
||||
channel-type.freeboxos.line-media.state.option.BACKUP_4G = Internet Backup
|
||||
channel-type.freeboxos.line-status.label = Line Status
|
||||
channel-type.freeboxos.line-status.description = Status of network line connection
|
||||
channel-type.freeboxos.line-status.state.option.GOING_UP = Connection is initializing
|
||||
channel-type.freeboxos.line-status.state.option.UP = Connection is active
|
||||
channel-type.freeboxos.line-status.state.option.GOING_DOWN = Connection is about to become inactive
|
||||
channel-type.freeboxos.line-status.state.option.DOWN = Connection is inactive
|
||||
channel-type.freeboxos.line-type.label = Line Type
|
||||
channel-type.freeboxos.line-type.description = Type of network line connection
|
||||
channel-type.freeboxos.line-type.state.option.ETHERNET = FTTH/ethernet
|
||||
channel-type.freeboxos.line-type.state.option.RFC2684 = xDSL (unbundled)
|
||||
channel-type.freeboxos.line-type.state.option.PPPOATM = xDSL
|
||||
channel-type.freeboxos.name.label = Name
|
||||
channel-type.freeboxos.name.description = Called name for outgoing calls. Caller name for incoming calls
|
||||
channel-type.freeboxos.number.label = Incoming Call
|
||||
channel-type.freeboxos.number.description = Details about call
|
||||
channel-type.freeboxos.onhook.label = On Hook
|
||||
channel-type.freeboxos.onhook.description = Indicates whether the phone is on hook
|
||||
channel-type.freeboxos.package.label = Active Package
|
||||
channel-type.freeboxos.package.description = Name of the package currently active on the player
|
||||
channel-type.freeboxos.phone-event.label = Phone Event
|
||||
channel-type.freeboxos.phone-event.description = Triggers when an event related to the phone has been detected
|
||||
channel-type.freeboxos.phone-number.label = Phone Number
|
||||
channel-type.freeboxos.player-status.label = Player Status
|
||||
channel-type.freeboxos.player-status.description = Status of the Freebox TV player
|
||||
channel-type.freeboxos.reachable.label = Reachable
|
||||
channel-type.freeboxos.reachable.description = Indicates if the network device is reachable or not
|
||||
channel-type.freeboxos.ringing.label = Ringing
|
||||
channel-type.freeboxos.ringing.description = Is the phone ringing
|
||||
channel-type.freeboxos.rssi.label = RSSI
|
||||
channel-type.freeboxos.rssi.description = Received signal strength indicator
|
||||
channel-type.freeboxos.samba-file-status.label = Windows File Sharing
|
||||
channel-type.freeboxos.samba-file-status.description = Status of Windows File Sharing (Samba)
|
||||
channel-type.freeboxos.samba-printer-status.label = Windows Printer Sharing
|
||||
channel-type.freeboxos.samba-printer-status.description = Status of Windows Printer Sharing
|
||||
channel-type.freeboxos.shutter.label = Shutter Position
|
||||
channel-type.freeboxos.shutter.description = Read / Write position of the shutter
|
||||
channel-type.freeboxos.ssid.label = SSID
|
||||
channel-type.freeboxos.status.label = VM Status
|
||||
channel-type.freeboxos.telephony-service.label = Telephony Service
|
||||
channel-type.freeboxos.telephony-service.description = Status of the telephony service
|
||||
channel-type.freeboxos.temperature.label = Temperature
|
||||
channel-type.freeboxos.temperature.description = Actual measured temperature of the sensor
|
||||
channel-type.freeboxos.timestamp.label = Timestamp
|
||||
channel-type.freeboxos.transfer-bytes.label = Transfered Bytes
|
||||
channel-type.freeboxos.transfer-bytes.description = Total data transfered since last connection
|
||||
channel-type.freeboxos.transfer-rate-bps.label = Transfer Rate
|
||||
channel-type.freeboxos.transfer-rate-bps.description = Current transfer rate
|
||||
channel-type.freeboxos.transfer-rate.label = Transfer Rate
|
||||
channel-type.freeboxos.transfer-rate.description = Current transfer rate
|
||||
channel-type.freeboxos.upnpav-status.label = UPnP AV Enabled
|
||||
channel-type.freeboxos.upnpav-status.description = Indicates whether UPnP AV is enabled
|
||||
channel-type.freeboxos.uptime.label = Uptime
|
||||
channel-type.freeboxos.uptime.description = Time since last reboot of the equipment
|
||||
channel-type.freeboxos.wifi-host.label = Access Point
|
||||
channel-type.freeboxos.wifi-status.label = Wifi Enabled
|
||||
channel-type.freeboxos.wifi-status.description = Indicates whether the wifi network is enabled
|
||||
|
||||
# messages
|
||||
|
||||
info-conf-pending = Please accept pairing request directly on your freebox
|
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="freeboxos"
|
||||
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="api">
|
||||
<label>Freebox OS Api</label>
|
||||
<description>Bridge between hosts and the API rest service</description>
|
||||
|
||||
<representation-property>apiDomain</representation-property>
|
||||
|
||||
<config-description-ref uri="bridge-type:freeboxos:api"/>
|
||||
</bridge-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -0,0 +1,465 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<thing:thing-descriptions bindingId="freeboxos"
|
||||
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">
|
||||
|
||||
<channel-type id="lcd-brightness" advanced="true">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Screen Brightness</label>
|
||||
<description>Brightness level of the screen in percent</description>
|
||||
<category>DimmableLight</category>
|
||||
<state pattern="%d %unit%" min="0" max="100"/>
|
||||
</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="rssi">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>RSSI</label>
|
||||
<description>Received signal strength indicator</description>
|
||||
<category>QualityOfService</category>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ssid" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>SSID</label>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="wifi-host" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Access Point</label>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="lcd-forced" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Forced Orientation</label>
|
||||
<description>Indicates whether the screen orientation is forced</description>
|
||||
<category>Switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dect-active">
|
||||
<item-type>Switch</item-type>
|
||||
<label>DECT Enabled</label>
|
||||
<description>Activates / stops the integrated DECT base</description>
|
||||
<category>Switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="alternate-ring">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Alternating Ring</label>
|
||||
<category>Switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperature" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>Actual measured temperature of the sensor</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fanspeed" advanced="true">
|
||||
<item-type>Number:Frequency</item-type>
|
||||
<label>Fan Speed</label>
|
||||
<description>Actual measured rotation speed of the fan</description>
|
||||
<category>Fan</category>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="samba-file-status" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Windows File Sharing</label>
|
||||
<description>Status of Windows File Sharing (Samba)</description>
|
||||
<category>Switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="afp-file-status" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Mac OS File Sharing</label>
|
||||
<description>Status of Mac OS File Sharing</description>
|
||||
<category>Switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="samba-printer-status" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Windows Printer Sharing</label>
|
||||
<description>Status of Windows Printer Sharing</description>
|
||||
<category>Switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="bandwidth-usage">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Bandwidth Usage</label>
|
||||
<description>Current bandwidth usage</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="transfer-rate">
|
||||
<item-type>Number:DataTransferRate</item-type>
|
||||
<label>Transfer Rate</label>
|
||||
<description>Current transfer rate</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="transfer-rate-bps" advanced="true">
|
||||
<item-type>Number:DataTransferRate</item-type>
|
||||
<label>Transfer Rate</label>
|
||||
<description>Current transfer rate</description>
|
||||
<state readOnly="true" pattern="%.2f bit/s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="transfer-bytes" advanced="true">
|
||||
<item-type>Number:DataAmount</item-type>
|
||||
<label>Transfered Bytes</label>
|
||||
<description>Total data transfered since last connection</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="bandwidth" advanced="true">
|
||||
<item-type>Number:DataTransferRate</item-type>
|
||||
<label>Bandwidth</label>
|
||||
<description>Available bandwidth</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="uptime" advanced="true">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Uptime</label>
|
||||
<description>Time since last reboot of the equipment</description>
|
||||
<category>time</category>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="line-status">
|
||||
<item-type>String</item-type>
|
||||
<label>Line Status</label>
|
||||
<description>Status of network line connection</description>
|
||||
<state readOnly="true" pattern="%s">
|
||||
<options>
|
||||
<option value="GOING_UP">Connection is initializing</option>
|
||||
<option value="UP">Connection is active</option>
|
||||
<option value="GOING_DOWN">Connection is about to become inactive</option>
|
||||
<option value="DOWN">Connection is inactive</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="line-type">
|
||||
<item-type>String</item-type>
|
||||
<label>Line Type</label>
|
||||
<description>Type of network line connection</description>
|
||||
<state readOnly="true" pattern="%s">
|
||||
<options>
|
||||
<option value="ETHERNET">FTTH/ethernet</option>
|
||||
<option value="RFC2684">xDSL (unbundled)</option>
|
||||
<option value="PPPOATM">xDSL</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="line-media">
|
||||
<item-type>String</item-type>
|
||||
<label>Line Media</label>
|
||||
<description>Media of network line connection</description>
|
||||
<state readOnly="true" pattern="%s">
|
||||
<options>
|
||||
<option value="FTTH">FTTH</option>
|
||||
<option value="ETHERNET">Ethernet</option>
|
||||
<option value="XDSL">xDSL</option>
|
||||
<option value="BACKUP_4G">Internet Backup</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="player-status">
|
||||
<item-type>String</item-type>
|
||||
<label>Player Status</label>
|
||||
<description>Status of the Freebox TV player</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="package">
|
||||
<item-type>String</item-type>
|
||||
<label>Active Package</label>
|
||||
<description>Name of the package currently active on the player</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</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">
|
||||
<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="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>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="phone-number">
|
||||
<item-type>String</item-type>
|
||||
<label>Phone Number</label>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="number">
|
||||
<item-type>Call</item-type>
|
||||
<label>Incoming Call</label>
|
||||
<description>Details about call</description>
|
||||
<state pattern="%1$s => %2$s" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="duration">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Duration</label>
|
||||
<description>Call duration in seconds</description>
|
||||
<category>time</category>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="timestamp">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Timestamp</label>
|
||||
<category>time</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="name" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Name</label>
|
||||
<description>Called name for outgoing calls. Caller name for incoming calls</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="reachable">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Reachable</label>
|
||||
<description>Indicates if the network device is reachable or not</description>
|
||||
<category>Switch</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="status">
|
||||
<item-type>Switch</item-type>
|
||||
<label>VM Status</label>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="box-event">
|
||||
<kind>trigger</kind>
|
||||
<label>Server Event</label>
|
||||
<description>Triggers when an event related to the Freebox server has been detected</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="phone-event">
|
||||
<kind>trigger</kind>
|
||||
<label>Phone Event</label>
|
||||
<description>Triggers when an event related to the phone has been detected</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="key-code">
|
||||
<item-type>String</item-type>
|
||||
<label>Remote Key Code</label>
|
||||
<description>Simulates pushing a remote control button</description>
|
||||
<state readOnly="false" pattern="%s">
|
||||
<options>
|
||||
<option value="red">Red</option>
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="yellow">Yellow</option>
|
||||
<option value="power">On/Off</option>
|
||||
<option value="list">List</option>
|
||||
<option value="tv">TV</option>
|
||||
<option value="0">0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="9">9</option>
|
||||
<option value="vol_inc">Volume Up</option>
|
||||
<option value="vol_dec">Volume Down</option>
|
||||
<option value="mute">Mute</option>
|
||||
<option value="prgm_inc">Prog Up</option>
|
||||
<option value="prgm_dec">Prog Down</option>
|
||||
<option value="prev">Previous</option>
|
||||
<option value="bwd">Backward</option>
|
||||
<option value="play">Play/Pause</option>
|
||||
<option value="rec">Record</option>
|
||||
<option value="fwd">Forward</option>
|
||||
<option value="next">Next</option>
|
||||
<option value="up">Up</option>
|
||||
<option value="right">Right</option>
|
||||
<option value="down">Down</option>
|
||||
<option value="left">Left</option>
|
||||
<option value="back">Back</option>
|
||||
<option value="swap">Swap</option>
|
||||
<option value="info">Info</option>
|
||||
<option value="epg">EPG</option>
|
||||
<option value="mail">Mail</option>
|
||||
<option value="media">Media</option>
|
||||
<option value="help">Help</option>
|
||||
<option value="options">Options</option>
|
||||
<option value="pip">PIP</option>
|
||||
<option value="ok">OK</option>
|
||||
<option value="home">Home</option>
|
||||
</options>
|
||||
</state>
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ip-address" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>IP Address</label>
|
||||
<description>IP address of the client</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="led">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Led Activated</label>
|
||||
<description>Led indicator status</description>
|
||||
<category>Switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="gain">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Gain</label>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="connection-status">
|
||||
<item-type>String</item-type>
|
||||
<label>Connection</label>
|
||||
<description>Connection Status</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="hardware-status">
|
||||
<item-type>String</item-type>
|
||||
<label>Hardware Status</label>
|
||||
<description>Hardware status of the phone line</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="telephony-service">
|
||||
<item-type>String</item-type>
|
||||
<label>Telephony Service</label>
|
||||
<description>Status of the telephony service</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="host-count">
|
||||
<item-type>Number</item-type>
|
||||
<label>Host Count</label>
|
||||
<description>Number of hosts connected to the device</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="keyfob-enable">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Keyfob Enabled</label>
|
||||
<description>Activates / deactivates the keyfob</description>
|
||||
<category>Switch</category>
|
||||
<config-description>
|
||||
<parameter name="slot" type="integer"/>
|
||||
<parameter name="signal" type="integer"/>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="basic-shutter">
|
||||
<item-type>Rollershutter</item-type>
|
||||
<label>Shutter</label>
|
||||
<description>Shutter command</description>
|
||||
<category>Blinds</category>
|
||||
<config-description>
|
||||
<parameter name="up" type="integer"/>
|
||||
<parameter name="down" type="integer"/>
|
||||
<parameter name="stop" type="integer"/>
|
||||
<parameter name="signal" type="integer"/>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="shutter">
|
||||
<item-type>Rollershutter</item-type>
|
||||
<label>Shutter Position</label>
|
||||
<description>Read / Write position of the shutter</description>
|
||||
<state pattern="%d %unit%"/>
|
||||
<config-description>
|
||||
<parameter name="slot" type="integer"/>
|
||||
<parameter name="signal" type="integer"/>
|
||||
<parameter name="stop" type="integer"/>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="alarm-timeout">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Alarm Duration</label>
|
||||
<category>oh:freeboxos:zone_temporisee</category>
|
||||
<state pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="alarm-volume">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Alarm Volume</label>
|
||||
<category>oh:freeboxos:sirene</category>
|
||||
<state min="0" max="100" step="1" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="alarm-pin">
|
||||
<item-type>String</item-type>
|
||||
<label>PIN Code</label>
|
||||
<category>oh:freeboxos:pin_code</category>
|
||||
<state pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="freeboxos"
|
||||
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">
|
||||
|
||||
<thing-type id="freeplug">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="api"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Freeplug</label>
|
||||
<description>Ethernet / CPL gateway</description>
|
||||
|
||||
<channels>
|
||||
<channel id="line-status" typeId="line-status"/>
|
||||
<channel id="last-seen" typeId="timestamp">
|
||||
<label>Last Activity</label>
|
||||
</channel>
|
||||
<channel id="reachable" typeId="reachable"/>
|
||||
<channel id="rate-up" typeId="transfer-rate">
|
||||
<label>Tx Rate</label>
|
||||
<description>Current TX Rate</description>
|
||||
</channel>
|
||||
<channel id="rate-down" typeId="transfer-rate">
|
||||
<label>Rx Rate</label>
|
||||
<description>Current RX rate</description>
|
||||
</channel>
|
||||
</channels>
|
||||
|
||||
<representation-property>macAddress</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:freeboxos:host"/>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user