mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[somneo] Initial contribution (#12321)
* [somneo] Initial contribution Signed-off-by: Michael Myrcik <michael.myrcik@web.de>
This commit is contained in:
parent
1181a104f9
commit
3b8567bd9e
@ -303,6 +303,7 @@
|
||||
/bundles/org.openhab.binding.solarwatt/ @sven-carstens
|
||||
/bundles/org.openhab.binding.somfymylink/ @loungeflyz
|
||||
/bundles/org.openhab.binding.somfytahoma/ @octa22
|
||||
/bundles/org.openhab.binding.somneo/ @0x4d4d
|
||||
/bundles/org.openhab.binding.sonnen/ @chingon007
|
||||
/bundles/org.openhab.binding.sonos/ @kgoderis @lolodomo
|
||||
/bundles/org.openhab.binding.sonyaudio/ @freke
|
||||
|
@ -1511,6 +1511,11 @@
|
||||
<artifactId>org.openhab.binding.somfytahoma</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.somneo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.sonnen</artifactId>
|
||||
|
13
bundles/org.openhab.binding.somneo/NOTICE
Normal file
13
bundles/org.openhab.binding.somneo/NOTICE
Normal file
@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
152
bundles/org.openhab.binding.somneo/README.md
Normal file
152
bundles/org.openhab.binding.somneo/README.md
Normal file
@ -0,0 +1,152 @@
|
||||
# Philips Somneo Binding
|
||||
|
||||
This binding integrates Philips Somneo HF367X into openHAB.
|
||||
|
||||
## Supported Things
|
||||
|
||||
This binding does only support one Thing:
|
||||
|
||||
- `Philips Somneo HF367X`: A connected sleep and wake-Up light with the ThingTypeUID `hf367x`
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The Philips Somneo thing requires the `hostname` it can connect to.
|
||||
Its API only allows HTTPS access, but unfortunately the SSL certificate is not trusted and must be ignored by the parameter.
|
||||
|
||||
| Parameter | Values | Default |
|
||||
|---------------------|-------------------------------------------|---------|
|
||||
| hostname | Hostname or IP address of the device | - |
|
||||
| port | Port number | 443 |
|
||||
| refreshInterval | Interval the device is polled in sec | 30 |
|
||||
| ignoreSSLErrors | Ignore SSL Errors | true |
|
||||
|
||||
## Channels
|
||||
|
||||
| Channel | Type | Read/Write | Description |
|
||||
|-----------------------|----------------------|------------|-------------------------------------------------------------|
|
||||
| _Sensor_ | | | |
|
||||
| sensor#illuminance | Number:Illuminance | R | The current illuminance in lux |
|
||||
| sensor#temperature | Number:Temperature | R | The current temperature |
|
||||
| sensor#humidity | Number:Dimensionless | R | The current humidity in % |
|
||||
| sensor#noise | Number:Dimensionless | R | The current noise in dB |
|
||||
| _Light_ | | | |
|
||||
| light#main | Switch | RW | Turn the light on, off and set the brightness |
|
||||
| light#night | Switch | RW | Turn the night light on or off |
|
||||
| _Sunset_ | | | |
|
||||
| sunset#switch | Switch | RW | Turn the sunset program on or off |
|
||||
| sunset#remainingTime | Number:Time | R | Remaining time from an activated program |
|
||||
| sunset#lightIntensity | Dimmer | RW | Set the brightness during the sunset programme |
|
||||
| sunset#duration | Number:Time | RW | The duration of sunset program in minutes |
|
||||
| sunset#colorSchema | Number | RW | Choose a personal sunset |
|
||||
| sunset#ambientNoise | String | RW | Ambient noise played during the sunset |
|
||||
| sunset#volume | Dimmer | RW | Set the volume during the sunset programme |
|
||||
| _Relax_ | | | |
|
||||
| relax#switch | Switch | RW | Turn the relax breathe program on or off |
|
||||
| relax#remainingTime | Number:Time | R | Remaining time from an activated program |
|
||||
| relax#breathingRate | Number | RW | Breathing rate per minute during the relax program |
|
||||
| relax#duration | Number:Time | RW | The duration of breathe program in minutes |
|
||||
| relax#guidanceType | Number | RW | Select a breath guidance type during the relax program |
|
||||
| relax#lightIntensity | Dimmer | RW | Set the brightness during the breathe programme |
|
||||
| relax#volume | Dimmer | RW | Set the volume during the breathe programme |
|
||||
| _Audio_ | | | |
|
||||
| audio#radio | Player | RW | Controlling the radio and seeking for a frequency |
|
||||
| audio#aux | Switch | RW | Turn the AUX input on or off |
|
||||
| audio#volume | Dimmer | RW | Change the sound volume of the device |
|
||||
| audio#preset | String | RW | The Device has 5 presets to store radio frequencies |
|
||||
| audio#frequency | String | R | The currently selected radio frequency |
|
||||
|
||||
## Full Example
|
||||
|
||||
somneo.things:
|
||||
|
||||
```
|
||||
Thing somneo:hf367x:1 "Philips Somneo" @ "Bedroom" [ hostname="192.168.0.110", ignoreSSLErrors=true ]
|
||||
```
|
||||
|
||||
somneo.items:
|
||||
|
||||
```
|
||||
// Sensors
|
||||
Number:Illuminance PhilipsSomneo_Illuminance "Illuminance" <Sun> ["Measurement", "Light"] { channel="somneo:hf367x:1:sensor#illuminance" }
|
||||
Number:Temperature PhilipsSomneo_Temperature "Temperature" <Temperature> ["Measurement", "Temperature"] { channel="somneo:hf367x:1:sensor#temperature" }
|
||||
Number:Dimensionless PhilipsSomneo_Humidity "Humidity" <Humidity> ["Measurement", "Humidity"] { channel="somneo:hf367x:1:sensor#humidity" }
|
||||
Number:Dimensionless PhilipsSomneo_Noise "Noise" <Noise> ["Measurement", "Noise"] { channel="somneo:hf367x:1:sensor#noise" }
|
||||
// Light
|
||||
Dimmer PhilipsSomneo_MainLight "Light" <Light> ["Control", "Light"] { channel="somneo:hf367x:1:light#main" }
|
||||
Switch PhilipsSomneo_NightLite "Night Light" <Light> ["Control", "Light"] { channel="somneo:hf367x:1:light#night" }
|
||||
// Sunset
|
||||
Switch PhilipsSomneo_SunsetSwitch "Sunset Program" <Light> ["Switch", "Power"] { channel="somneo:hf367x:1:sunset#switch" }
|
||||
Number:Time PhilipsSomneo_SunsetRemaining "Remaining Time" <Time> ["Status", "Duration"] { channel="somneo:hf367x:1:sunset#remainingTime" }
|
||||
Dimmer PhilipsSomneo_SunsetIntensity "Light Intensity" <Light> ["Control", "Light"] { channel="somneo:hf367x:1:sunset#lightIntensity" }
|
||||
Number:Time PhilipsSomneo_SunsetDuration "Duration" <Time> ["Control", "Duration"] { channel="somneo:hf367x:1:sunset#duration" }
|
||||
Number PhilipsSomneo_SunsetColor "Sunset Color" <Sunset> ["Control", "ColorTemperature"] { channel="somneo:hf367x:1:sunset#colorSchema" }
|
||||
String PhilipsSomneo_SunsetNoise "Ambient Noise" <Noise> ["Control", "Noise"] { channel="somneo:hf367x:1:sunset#ambientNoise" }
|
||||
Dimmer PhilipsSomneo_SunsetVolume "Volume" <SoundVolume> ["Control", "SoundVolume"] { channel="somneo:hf367x:1:sunset#volume" }
|
||||
// Relax
|
||||
Switch PhilipsSomneo_RelaxSwitch "Relax Program" <Light> ["Switch", "Power"] { channel="somneo:hf367x:1:relax#switch" }
|
||||
Number:Time PhilipsSomneo_RelaxRemaining "Remaining Time" <Time> ["Status", "Duration"] { channel="somneo:hf367x:1:relax#remainingTime" }
|
||||
Number PhilipsSomneo_RelaxBreathingRate "Breathing Rate" ["Control"] { channel="somneo:hf367x:1:relax#breathingRate" }
|
||||
Number:Time PhilipsSomneo_RelaxDuration "Duration" <Time> ["Control", "Duration"] { channel="somneo:hf367x:1:relax#duration" }
|
||||
Number PhilipsSomneo_RelaxGuidanceType "Guidance Type" ["Control"] { channel="somneo:hf367x:1:relax#guidanceType" }
|
||||
Dimmer PhilipsSomneo_RelaxIntensity "Light Intensity" <Light> ["Control", "Light"] { channel="somneo:hf367x:1:relax#lightIntensity" }
|
||||
Dimmer PhilipsSomneo_RelaxVolume "Volume" <SoundVolume> ["Control", "SoundVolume"] { channel="somneo:hf367x:1:relax#volume" }
|
||||
// Audio
|
||||
Player PhilipsSomneo_AudioRadio "Radio Control" <MediaControl> ["Control"] { channel="somneo:hf367x:1:audio#radio" }
|
||||
Switch PhilipsSomneo_AudioAux "AUX-Input" ["Switch", "Power"] { channel="somneo:hf367x:1:audio#aux" }
|
||||
Dimmer PhilipsSomneo_AudioVolume "Volume" <SoundVolume> ["Control", "SoundVolume"] { channel="somneo:hf367x:1:audio#volume" }
|
||||
String PhilipsSomneo_AudioPreset "FM Preset" ["Control"] { channel="somneo:hf367x:1:audio#preset" }
|
||||
String PhilipsSomneo_AudioFrequency "FM Frequency" ["Status"] { channel="somneo:hf367x:1:audio#frequency" }
|
||||
```
|
||||
|
||||
somneo.sitemap:
|
||||
|
||||
```
|
||||
sitemap somneo label="Philips Somneo" {
|
||||
Frame label="Sensors" {
|
||||
Default item=PhilipsSomneo_Illuminance
|
||||
Default item=PhilipsSomneo_Temperature
|
||||
Default item=PhilipsSomneo_Humidity
|
||||
Default item=PhilipsSomneo_Noise
|
||||
}
|
||||
Frame label="Lights" {
|
||||
Default item=PhilipsSomneo_MainLight
|
||||
Default item=PhilipsSomneo_MainLight visibility=[PhilipsSomneo_MainLight>0]
|
||||
Default item=PhilipsSomneo_NightLite
|
||||
}
|
||||
Frame label="Programs" {
|
||||
Default item=PhilipsSomneo_SunsetSwitch
|
||||
Default item=PhilipsSomneo_SunsetRemaining visibility=[PhilipsSomneo_SunsetSwitch==ON]
|
||||
Text label="Sunset Settings" icon="settings" {
|
||||
Default item=PhilipsSomneo_SunsetIntensity
|
||||
Default item=PhilipsSomneo_SunsetDuration
|
||||
Selection item=PhilipsSomneo_SunsetColor
|
||||
Default item=PhilipsSomneo_SunsetNoise
|
||||
Default item=PhilipsSomneo_SunsetVolume
|
||||
}
|
||||
Default item=PhilipsSomneo_RelaxSwitch
|
||||
Default item=PhilipsSomneo_RelaxRemaining visibility=[PhilipsSomneo_RelaxSwitch==ON]
|
||||
Text label="Relax Settings" icon="settings" {
|
||||
Default item=PhilipsSomneo_RelaxBreathingRate
|
||||
Selection item=PhilipsSomneo_RelaxDuration
|
||||
Switch item=PhilipsSomneo_RelaxGuidanceType mappings=[0="Light", 1="Sound"]
|
||||
Default item=PhilipsSomneo_RelaxIntensity
|
||||
Default item=PhilipsSomneo_RelaxVolume
|
||||
}
|
||||
}
|
||||
Frame label="Audio" {
|
||||
Default item=PhilipsSomneo_AudioRadio
|
||||
Default item=PhilipsSomneo_AudioAux
|
||||
Default item=PhilipsSomneo_AudioVolume visibility=[PhilipsSomneo_AudioRadio==PLAY, PhilipsSomneo_AudioAux==ON]
|
||||
Default item=PhilipsSomneo_AudioPreset visibility=[PhilipsSomneo_AudioRadio==PLAY]
|
||||
Default item=PhilipsSomneo_AudioFrequency visibility=[PhilipsSomneo_AudioRadio==PLAY]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Thanks to:
|
||||
|
||||
* [homebridge-somneo](https://github.com/zackwag/homebridge-somneo) - For creating a similar plugin in another platform and exposing endpoints for control.
|
||||
* [somneo-client](https://github.com/DonkerNet/somneo-client) - For creating a similar plugin in another platform and exposing endpoints for control.
|
||||
* HTTP Binding and other OpenHAB addons - Which was used as examples.
|
17
bundles/org.openhab.binding.somneo/pom.xml
Normal file
17
bundles/org.openhab.binding.somneo/pom.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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>3.4.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.somneo</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Somneo Binding</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.somneo-${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-somneo" description="Somneo Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.somneo/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
|
||||
/**
|
||||
* The {@link HttpClientProvider} defines the interface for providing
|
||||
* {@link HttpClient} instances to thing handlers.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface HttpClientProvider {
|
||||
|
||||
/**
|
||||
* Get the secure http client
|
||||
*
|
||||
* @return a HttpClient
|
||||
*/
|
||||
HttpClient getSecureClient();
|
||||
|
||||
/**
|
||||
* Get the insecure http client (ignores SSL errors)
|
||||
*
|
||||
* @return a HttpClient
|
||||
*/
|
||||
HttpClient getInsecureClient();
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link SomneoBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SomneoBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "somneo";
|
||||
|
||||
// List of all Thing properties
|
||||
public static final String PROPERTY_VENDOR_NAME = "Philips";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_HF367X = new ThingTypeUID(BINDING_ID, "hf367x");
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String CHANNEL_AUDIO_AUX = "audio#aux";
|
||||
public static final String CHANNEL_AUDIO_FREQUENCY = "audio#frequency";
|
||||
public static final String CHANNEL_AUDIO_PRESET = "audio#preset";
|
||||
public static final String CHANNEL_AUDIO_RADIO = "audio#radio";
|
||||
public static final String CHANNEL_AUDIO_VOLUME = "audio#volume";
|
||||
public static final String CHANNEL_LIGHT_MAIN = "light#main";
|
||||
public static final String CHANNEL_LIGHT_NIGHT = "light#night";
|
||||
public static final String CHANNEL_RELAX_BREATHING_RATE = "relax#breathingRate";
|
||||
public static final String CHANNEL_RELAX_DURATION = "relax#duration";
|
||||
public static final String CHANNEL_RELAX_GUIDANCE_TYPE = "relax#guidanceType";
|
||||
public static final String CHANNEL_RELAX_LIGHT_INTENSITY = "relax#lightIntensity";
|
||||
public static final String CHANNEL_RELAX_REMAINING_TIME = "relax#remainingTime";
|
||||
public static final String CHANNEL_RELAX_SWITCH = "relax#switch";
|
||||
public static final String CHANNEL_RELAX_VOLUME = "relax#volume";
|
||||
public static final String CHANNEL_SENSOR_ILLUMINANCE = "sensor#illuminance";
|
||||
public static final String CHANNEL_SENSOR_HUMIDITY = "sensor#humidity";
|
||||
public static final String CHANNEL_SENSOR_NOISE = "sensor#noise";
|
||||
public static final String CHANNEL_SENSOR_TEMPERATURE = "sensor#temperature";
|
||||
public static final String CHANNEL_SUNSET_AMBIENT_NOISE = "sunset#ambientNoise";
|
||||
public static final String CHANNEL_SUNSET_COLOR_SCHEMA = "sunset#colorSchema";
|
||||
public static final String CHANNEL_SUNSET_DURATION = "sunset#duration";
|
||||
public static final String CHANNEL_SUNSET_LIGHT_INTENSITY = "sunset#lightIntensity";
|
||||
public static final String CHANNEL_SUNSET_REMAINING_TIME = "sunset#remainingTime";
|
||||
public static final String CHANNEL_SUNSET_SWITCH = "sunset#switch";
|
||||
public static final String CHANNEL_SUNSET_VOLUME = "sunset#volume";
|
||||
|
||||
// List of all Web Service Endpoints
|
||||
public static final String AUDIO_ENDPOINT = "/1/wuply";
|
||||
public static final String DEVICE_ENDPOINT = "/1/device";
|
||||
public static final String FIRMWARE_ENDPOINT = "/0/firmware";
|
||||
public static final String LIGHT_ENDPOINT = "/1/wulgt";
|
||||
public static final String PRESET_ENDPOINT = "/1/wufmp/00";
|
||||
public static final String RADIO_ENDPOINT = "/1/wufmr";
|
||||
public static final String RELAX_ENDPOINT = "/1/wurlx";
|
||||
public static final String TIMER_ENDPOINT = "/1/wutmr";
|
||||
public static final String SENSORS_ENDPOINT = "/1/wusrd";
|
||||
public static final String SUNSET_ENDPOINT = "/1/wudsk";
|
||||
public static final String WIFI_ENDPOINT = "/0/wifi";
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SomneoConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SomneoConfiguration {
|
||||
|
||||
public String hostname = "";
|
||||
public int port = 443;
|
||||
public int refreshInterval = 30;
|
||||
public boolean ignoreSSLErrors = false;
|
||||
}
|
@ -0,0 +1,544 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal;
|
||||
|
||||
import static org.openhab.binding.somneo.internal.SomneoBindingConstants.*;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
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.openhab.binding.somneo.internal.model.AudioData;
|
||||
import org.openhab.binding.somneo.internal.model.DeviceData;
|
||||
import org.openhab.binding.somneo.internal.model.FirmwareData;
|
||||
import org.openhab.binding.somneo.internal.model.LightData;
|
||||
import org.openhab.binding.somneo.internal.model.PresetData;
|
||||
import org.openhab.binding.somneo.internal.model.RadioData;
|
||||
import org.openhab.binding.somneo.internal.model.RelaxData;
|
||||
import org.openhab.binding.somneo.internal.model.SensorData;
|
||||
import org.openhab.binding.somneo.internal.model.SunsetData;
|
||||
import org.openhab.binding.somneo.internal.model.TimerData;
|
||||
import org.openhab.binding.somneo.internal.model.WifiData;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.NextPreviousType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.PlayPauseType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link SomneoHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SomneoHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(SomneoHandler.class);
|
||||
|
||||
private final HttpClientProvider httpClientProvider;
|
||||
|
||||
private final SomneoPresetStateDescriptionProvider provider;
|
||||
|
||||
/**
|
||||
* Job to poll data from the device.
|
||||
*/
|
||||
private @Nullable ScheduledFuture<?> pollingJob;
|
||||
|
||||
/**
|
||||
* Job to count down the remaining program time.
|
||||
*/
|
||||
private @Nullable ScheduledFuture<?> remainingTimerJob;
|
||||
|
||||
private @Nullable SomneoHttpConnector connector;
|
||||
|
||||
/**
|
||||
* Cache the last brightness level in order to know the correct level when the
|
||||
* ON command is given.
|
||||
*/
|
||||
private volatile int lastLightBrightness;
|
||||
|
||||
private volatile int remainingTimeRelax;
|
||||
|
||||
private volatile int remainingTimeSunset;
|
||||
|
||||
public SomneoHandler(Thing thing, HttpClientProvider httpClientProvider,
|
||||
SomneoPresetStateDescriptionProvider provider) {
|
||||
super(thing);
|
||||
this.httpClientProvider = httpClientProvider;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
String channelId = channelUID.getId();
|
||||
logger.debug("Handle command '{}' for channel {}", command, channelId);
|
||||
|
||||
if (command instanceof RefreshType) {
|
||||
this.poll();
|
||||
return;
|
||||
}
|
||||
|
||||
final SomneoHttpConnector connector = this.connector;
|
||||
if (connector == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch (channelId) {
|
||||
case CHANNEL_AUDIO_AUX:
|
||||
if (command instanceof OnOffType) {
|
||||
boolean isOn = OnOffType.ON.equals(command);
|
||||
connector.switchAux(isOn);
|
||||
|
||||
if (isOn) {
|
||||
updateState(CHANNEL_AUDIO_RADIO, PlayPauseType.PAUSE);
|
||||
updateState(CHANNEL_RELAX_SWITCH, OnOffType.OFF);
|
||||
updateState(CHANNEL_SUNSET_SWITCH, OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHANNEL_AUDIO_PRESET:
|
||||
if (command instanceof StringType) {
|
||||
connector.setRadioChannel(command.toFullString());
|
||||
|
||||
updateState(CHANNEL_AUDIO_RADIO, PlayPauseType.PLAY);
|
||||
updateState(CHANNEL_AUDIO_AUX, OnOffType.OFF);
|
||||
updateState(CHANNEL_RELAX_SWITCH, OnOffType.OFF);
|
||||
updateState(CHANNEL_SUNSET_SWITCH, OnOffType.OFF);
|
||||
|
||||
updateFrequency();
|
||||
}
|
||||
break;
|
||||
case CHANNEL_AUDIO_RADIO:
|
||||
if (command instanceof PlayPauseType) {
|
||||
boolean isPlaying = PlayPauseType.PLAY.equals(command);
|
||||
connector.switchRadio(isPlaying);
|
||||
|
||||
if (isPlaying) {
|
||||
updateState(CHANNEL_AUDIO_AUX, OnOffType.OFF);
|
||||
updateState(CHANNEL_RELAX_SWITCH, OnOffType.OFF);
|
||||
updateState(CHANNEL_SUNSET_SWITCH, OnOffType.OFF);
|
||||
}
|
||||
} else if (command instanceof NextPreviousType && NextPreviousType.NEXT.equals(command)) {
|
||||
connector.radioSeekUp();
|
||||
|
||||
updateFrequency();
|
||||
} else if (command instanceof NextPreviousType && NextPreviousType.PREVIOUS.equals(command)) {
|
||||
connector.radioSeekDown();
|
||||
|
||||
updateFrequency();
|
||||
}
|
||||
break;
|
||||
case CHANNEL_AUDIO_VOLUME:
|
||||
if (command instanceof PercentType) {
|
||||
connector.setAudioVolume(Integer.parseInt(command.toFullString()));
|
||||
}
|
||||
break;
|
||||
case CHANNEL_LIGHT_MAIN:
|
||||
if (command instanceof OnOffType) {
|
||||
boolean isOn = OnOffType.ON.equals(command);
|
||||
connector.switchMainLight(isOn);
|
||||
|
||||
if (isOn) {
|
||||
updateState(CHANNEL_LIGHT_MAIN, new PercentType(lastLightBrightness));
|
||||
updateState(CHANNEL_LIGHT_NIGHT, OnOffType.OFF);
|
||||
updateState(CHANNEL_RELAX_SWITCH, OnOffType.OFF);
|
||||
updateState(CHANNEL_SUNSET_SWITCH, OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
if (command instanceof PercentType) {
|
||||
int level = Integer.parseInt(command.toFullString());
|
||||
|
||||
if (level > 0) {
|
||||
connector.setMainLightDimmer(level);
|
||||
lastLightBrightness = level;
|
||||
|
||||
updateState(CHANNEL_LIGHT_NIGHT, OnOffType.OFF);
|
||||
updateState(CHANNEL_RELAX_SWITCH, OnOffType.OFF);
|
||||
updateState(CHANNEL_SUNSET_SWITCH, OnOffType.OFF);
|
||||
} else {
|
||||
connector.switchMainLight(false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHANNEL_LIGHT_NIGHT:
|
||||
if (command instanceof OnOffType) {
|
||||
boolean isOn = OnOffType.ON.equals(command);
|
||||
connector.switchNightLight(isOn);
|
||||
|
||||
if (isOn) {
|
||||
updateState(CHANNEL_LIGHT_MAIN, OnOffType.OFF);
|
||||
updateState(CHANNEL_RELAX_SWITCH, OnOffType.OFF);
|
||||
updateState(CHANNEL_SUNSET_SWITCH, OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHANNEL_RELAX_BREATHING_RATE:
|
||||
if (command instanceof DecimalType) {
|
||||
connector.setRelaxBreathingRate(Integer.parseInt(command.toFullString()));
|
||||
}
|
||||
break;
|
||||
case CHANNEL_RELAX_DURATION:
|
||||
if (command instanceof DecimalType) {
|
||||
connector.setRelaxDuration(Integer.parseInt(command.toFullString()));
|
||||
}
|
||||
break;
|
||||
case CHANNEL_RELAX_GUIDANCE_TYPE:
|
||||
if (command instanceof DecimalType) {
|
||||
connector.setRelaxGuidanceType(Integer.parseInt(command.toFullString()));
|
||||
}
|
||||
break;
|
||||
case CHANNEL_RELAX_LIGHT_INTENSITY:
|
||||
if (command instanceof PercentType) {
|
||||
connector.setRelaxLightIntensity(Integer.parseInt(command.toFullString()));
|
||||
}
|
||||
break;
|
||||
case CHANNEL_RELAX_SWITCH:
|
||||
if (command instanceof OnOffType) {
|
||||
boolean isOn = OnOffType.ON.equals(command);
|
||||
connector.switchRelaxProgram(isOn);
|
||||
|
||||
updateRemainingTimer();
|
||||
|
||||
if (isOn) {
|
||||
updateState(CHANNEL_AUDIO_AUX, OnOffType.OFF);
|
||||
updateState(CHANNEL_AUDIO_RADIO, PlayPauseType.PAUSE);
|
||||
updateState(CHANNEL_LIGHT_MAIN, OnOffType.OFF);
|
||||
updateState(CHANNEL_LIGHT_NIGHT, OnOffType.OFF);
|
||||
updateState(CHANNEL_SUNSET_SWITCH, OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHANNEL_RELAX_VOLUME:
|
||||
if (command instanceof PercentType) {
|
||||
connector.setRelaxVolume(Integer.parseInt(command.toFullString()));
|
||||
}
|
||||
break;
|
||||
case CHANNEL_SUNSET_AMBIENT_NOISE:
|
||||
if (command instanceof StringType) {
|
||||
connector.setSunsetAmbientNoise(command.toFullString());
|
||||
}
|
||||
break;
|
||||
case CHANNEL_SUNSET_COLOR_SCHEMA:
|
||||
if (command instanceof DecimalType) {
|
||||
connector.setSunsetColorSchema(Integer.parseInt(command.toFullString()));
|
||||
}
|
||||
break;
|
||||
case CHANNEL_SUNSET_DURATION:
|
||||
if (command instanceof DecimalType) {
|
||||
connector.setSunsetDuration(Integer.parseInt(command.toFullString()));
|
||||
}
|
||||
break;
|
||||
case CHANNEL_SUNSET_LIGHT_INTENSITY:
|
||||
if (command instanceof PercentType) {
|
||||
connector.setSunsetLightIntensity(Integer.parseInt(command.toFullString()));
|
||||
}
|
||||
break;
|
||||
case CHANNEL_SUNSET_SWITCH:
|
||||
if (command instanceof OnOffType) {
|
||||
boolean isOn = OnOffType.ON.equals(command);
|
||||
connector.switchSunsetProgram(isOn);
|
||||
|
||||
updateRemainingTimer();
|
||||
|
||||
if (isOn) {
|
||||
updateState(CHANNEL_AUDIO_AUX, OnOffType.OFF);
|
||||
updateState(CHANNEL_AUDIO_RADIO, PlayPauseType.PAUSE);
|
||||
updateState(CHANNEL_LIGHT_MAIN, OnOffType.OFF);
|
||||
updateState(CHANNEL_LIGHT_NIGHT, OnOffType.OFF);
|
||||
updateState(CHANNEL_RELAX_SWITCH, OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHANNEL_SUNSET_VOLUME:
|
||||
if (command instanceof PercentType) {
|
||||
connector.setSunsetVolume(Integer.parseInt(command.toFullString()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.warn("Received unknown channel {}", channelId);
|
||||
break;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("Handle command interrupted");
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (TimeoutException | ExecutionException e) {
|
||||
if (e.getCause() instanceof EOFException) {
|
||||
// Occurs on parallel mobile app access
|
||||
logger.debug("EOF: {}", e.getMessage());
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
initConnector();
|
||||
updateThingProperties();
|
||||
startPolling();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
stopPolling();
|
||||
stopRemainingTimer();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private void initConnector() {
|
||||
if (connector == null) {
|
||||
SomneoConfiguration config = getConfigAs(SomneoConfiguration.class);
|
||||
HttpClient httpClient;
|
||||
if (config.ignoreSSLErrors) {
|
||||
logger.info("Using the insecure client for thing '{}'.", thing.getUID());
|
||||
httpClient = httpClientProvider.getInsecureClient();
|
||||
} else {
|
||||
logger.info("Using the secure client for thing '{}'.", thing.getUID());
|
||||
httpClient = httpClientProvider.getSecureClient();
|
||||
}
|
||||
|
||||
connector = new SomneoHttpConnector(config, httpClient);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateThingProperties() {
|
||||
final SomneoHttpConnector connector = this.connector;
|
||||
if (connector == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String> properties = editProperties();
|
||||
properties.put(Thing.PROPERTY_VENDOR, PROPERTY_VENDOR_NAME);
|
||||
|
||||
try {
|
||||
final DeviceData deviceData = connector.fetchDeviceData();
|
||||
String value = deviceData.getModelId();
|
||||
if (value != null) {
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, value);
|
||||
}
|
||||
value = deviceData.getSerial();
|
||||
if (value != null) {
|
||||
properties.put(Thing.PROPERTY_SERIAL_NUMBER, value);
|
||||
}
|
||||
|
||||
final WifiData wifiData = connector.fetchWifiData();
|
||||
value = wifiData.getMacAddress();
|
||||
if (value != null) {
|
||||
properties.put(Thing.PROPERTY_MAC_ADDRESS, value);
|
||||
}
|
||||
|
||||
final FirmwareData firmwareData = connector.fetchFirmwareData();
|
||||
value = firmwareData.getVersion();
|
||||
if (value != null) {
|
||||
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, value);
|
||||
}
|
||||
|
||||
updateProperties(properties);
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("Update properties interrupted");
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (TimeoutException | ExecutionException e) {
|
||||
if (e.getCause() instanceof EOFException) {
|
||||
// Occurs on parallel mobile app access
|
||||
logger.debug("EOF: {}", e.getMessage());
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the connection to the receiver by starting to poll the HTTP API.
|
||||
*/
|
||||
private void startPolling() {
|
||||
final ScheduledFuture<?> pollingJob = this.pollingJob;
|
||||
if (pollingJob != null && !pollingJob.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int refreshInterval = getConfigAs(SomneoConfiguration.class).refreshInterval;
|
||||
logger.debug("Start polling job at interval {}s", refreshInterval);
|
||||
this.pollingJob = scheduler.scheduleWithFixedDelay(this::poll, 0, refreshInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void stopPolling() {
|
||||
final ScheduledFuture<?> pollingJob = this.pollingJob;
|
||||
if (pollingJob == null || pollingJob.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pollingJob.cancel(true);
|
||||
this.pollingJob = null;
|
||||
logger.debug("HTTP polling stopped.");
|
||||
}
|
||||
|
||||
private void poll() {
|
||||
final SomneoHttpConnector connector = this.connector;
|
||||
if (connector == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final SensorData sensorData = connector.fetchSensorData();
|
||||
updateState(CHANNEL_SENSOR_HUMIDITY, sensorData.getCurrentHumidity());
|
||||
updateState(CHANNEL_SENSOR_ILLUMINANCE, sensorData.getCurrentIlluminance());
|
||||
updateState(CHANNEL_SENSOR_NOISE, sensorData.getCurrentNoise());
|
||||
updateState(CHANNEL_SENSOR_TEMPERATURE, sensorData.getCurrentTemperature());
|
||||
|
||||
final LightData lightData = connector.fetchLightData();
|
||||
updateState(CHANNEL_LIGHT_MAIN, lightData.getMainLightState());
|
||||
updateState(CHANNEL_LIGHT_NIGHT, lightData.getNightLightState());
|
||||
lastLightBrightness = lightData.getMainLightLevel();
|
||||
|
||||
final SunsetData sunsetData = connector.fetchSunsetData();
|
||||
updateState(CHANNEL_SUNSET_SWITCH, sunsetData.getSwitchState());
|
||||
updateState(CHANNEL_SUNSET_LIGHT_INTENSITY, sunsetData.getLightIntensity());
|
||||
updateState(CHANNEL_SUNSET_DURATION, sunsetData.getDurationInMin());
|
||||
updateState(CHANNEL_SUNSET_COLOR_SCHEMA, sunsetData.getColorSchema());
|
||||
updateState(CHANNEL_SUNSET_AMBIENT_NOISE, sunsetData.getAmbientNoise());
|
||||
updateState(CHANNEL_SUNSET_VOLUME, sunsetData.getSoundVolume());
|
||||
|
||||
final RelaxData relaxData = connector.fetchRelaxData();
|
||||
updateState(CHANNEL_RELAX_SWITCH, relaxData.getSwitchState());
|
||||
updateState(CHANNEL_RELAX_BREATHING_RATE, relaxData.getBreathingRate());
|
||||
updateState(CHANNEL_RELAX_DURATION, relaxData.getDurationInMin());
|
||||
updateState(CHANNEL_RELAX_GUIDANCE_TYPE, relaxData.getGuidanceType());
|
||||
updateState(CHANNEL_RELAX_LIGHT_INTENSITY, relaxData.getLightIntensity());
|
||||
updateState(CHANNEL_RELAX_VOLUME, relaxData.getSoundVolume());
|
||||
|
||||
final AudioData audioData = connector.fetchAudioData();
|
||||
updateState(CHANNEL_AUDIO_RADIO, audioData.getRadioState());
|
||||
updateState(CHANNEL_AUDIO_AUX, audioData.getAuxState());
|
||||
updateState(CHANNEL_AUDIO_VOLUME, audioData.getVolumeState());
|
||||
updateState(CHANNEL_AUDIO_PRESET, audioData.getPresetState());
|
||||
|
||||
updateFrequency();
|
||||
|
||||
updateRemainingTimer();
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("Polling data interrupted");
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (TimeoutException | ExecutionException e) {
|
||||
if (e.getCause() instanceof EOFException) {
|
||||
// Occurs on parallel mobile app access
|
||||
logger.debug("EOF: {}", e.getMessage());
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFrequency() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final SomneoHttpConnector connector = this.connector;
|
||||
if (connector == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
RadioData radioData = connector.getRadioData();
|
||||
updateState(CHANNEL_AUDIO_FREQUENCY, radioData.getFrequency());
|
||||
|
||||
final PresetData presetData = connector.fetchPresetData();
|
||||
final Channel presetChannel = getThing().getChannel(CHANNEL_AUDIO_PRESET);
|
||||
if (presetChannel != null) {
|
||||
provider.setStateOptions(presetChannel.getUID(), presetData.createPresetOptions());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRemainingTimer() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final SomneoHttpConnector connector = this.connector;
|
||||
if (connector == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TimerData timerData = connector.fetchTimerData();
|
||||
|
||||
remainingTimeRelax = timerData.remainingTimeRelax();
|
||||
remainingTimeSunset = timerData.remainingTimeSunset();
|
||||
|
||||
if (remainingTimeRelax > 0 || remainingTimeSunset > 0) {
|
||||
startRemainingTimer();
|
||||
} else {
|
||||
State state = new QuantityType<>(0, Units.SECOND);
|
||||
updateState(CHANNEL_RELAX_REMAINING_TIME, state);
|
||||
updateState(CHANNEL_SUNSET_REMAINING_TIME, state);
|
||||
}
|
||||
}
|
||||
|
||||
private void startRemainingTimer() {
|
||||
final ScheduledFuture<?> remainingTimerJob = this.remainingTimerJob;
|
||||
if (remainingTimerJob != null && !remainingTimerJob.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Start remaining timer ticker job");
|
||||
this.remainingTimerJob = scheduler.scheduleWithFixedDelay(this::remainingTimerTick, 0, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void stopRemainingTimer() {
|
||||
final ScheduledFuture<?> remainingTimerJob = this.remainingTimerJob;
|
||||
if (remainingTimerJob == null || remainingTimerJob.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
remainingTimerJob.cancel(true);
|
||||
this.remainingTimerJob = null;
|
||||
logger.debug("Remaining timer ticker stopped.");
|
||||
}
|
||||
|
||||
private void remainingTimerTick() {
|
||||
if (remainingTimeRelax > 0) {
|
||||
remainingTimeRelax--;
|
||||
|
||||
State state = new QuantityType<>(remainingTimeRelax, Units.SECOND);
|
||||
updateState(CHANNEL_RELAX_REMAINING_TIME, state);
|
||||
}
|
||||
|
||||
if (remainingTimeSunset > 0) {
|
||||
remainingTimeSunset--;
|
||||
|
||||
State state = new QuantityType<>(remainingTimeSunset, Units.SECOND);
|
||||
updateState(CHANNEL_SUNSET_REMAINING_TIME, state);
|
||||
}
|
||||
|
||||
if (remainingTimeRelax <= 0 && remainingTimeSunset <= 0) {
|
||||
stopRemainingTimer();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal;
|
||||
|
||||
import static org.openhab.binding.somneo.internal.SomneoBindingConstants.THING_TYPE_HF367X;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
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.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link SomneoHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.somneo", service = ThingHandlerFactory.class)
|
||||
public class SomneoHandlerFactory extends BaseThingHandlerFactory implements HttpClientProvider {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(SomneoHandlerFactory.class);
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_HF367X);
|
||||
|
||||
private final HttpClient secureClient;
|
||||
private final HttpClient insecureClient;
|
||||
private final SomneoPresetStateDescriptionProvider provider;
|
||||
|
||||
@Activate
|
||||
public SomneoHandlerFactory(@Reference SomneoPresetStateDescriptionProvider provider) {
|
||||
this.provider = provider;
|
||||
|
||||
this.secureClient = new HttpClient(new SslContextFactory.Client(false));
|
||||
this.insecureClient = new HttpClient(new SslContextFactory.Client(true));
|
||||
|
||||
try {
|
||||
this.secureClient.start();
|
||||
this.insecureClient.start();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to start insecure http client: {}", e.getMessage());
|
||||
throw new IllegalStateException("Could not create insecure HttpClient");
|
||||
}
|
||||
}
|
||||
|
||||
@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 (THING_TYPE_HF367X.equals(thingTypeUID)) {
|
||||
return new SomneoHandler(thing, this, provider);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClient getSecureClient() {
|
||||
return secureClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClient getInsecureClient() {
|
||||
return insecureClient;
|
||||
}
|
||||
}
|
@ -0,0 +1,356 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal;
|
||||
|
||||
import static org.openhab.binding.somneo.internal.SomneoBindingConstants.*;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
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.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.somneo.internal.model.AudioData;
|
||||
import org.openhab.binding.somneo.internal.model.DeviceData;
|
||||
import org.openhab.binding.somneo.internal.model.FirmwareData;
|
||||
import org.openhab.binding.somneo.internal.model.LightData;
|
||||
import org.openhab.binding.somneo.internal.model.PresetData;
|
||||
import org.openhab.binding.somneo.internal.model.RadioData;
|
||||
import org.openhab.binding.somneo.internal.model.RelaxData;
|
||||
import org.openhab.binding.somneo.internal.model.SensorData;
|
||||
import org.openhab.binding.somneo.internal.model.SunsetData;
|
||||
import org.openhab.binding.somneo.internal.model.TimerData;
|
||||
import org.openhab.binding.somneo.internal.model.WifiData;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The {@link SomneoHttpConnector} is responsible for sending commands.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SomneoHttpConnector {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(SomneoHttpConnector.class);
|
||||
|
||||
private static final int REQUEST_TIMEOUT_MS = 5000;
|
||||
|
||||
private static final String DEFAULT_CONTENT_TYPE = "application/json";
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
private final String urlBase;
|
||||
|
||||
public SomneoHttpConnector(SomneoConfiguration config, HttpClient httpClient) {
|
||||
this.httpClient = httpClient;
|
||||
this.urlBase = String.format("https://%s:%d/di/v1/products", config.hostname, config.port);
|
||||
}
|
||||
|
||||
public SensorData fetchSensorData() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
return executeUrl("GET", SENSORS_ENDPOINT, SensorData.class);
|
||||
}
|
||||
|
||||
public LightData fetchLightData() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
return executeUrl("GET", LIGHT_ENDPOINT, LightData.class);
|
||||
}
|
||||
|
||||
public SunsetData fetchSunsetData() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
return executeUrl("GET", SUNSET_ENDPOINT, SunsetData.class);
|
||||
}
|
||||
|
||||
public void switchMainLight(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final LightData data = new LightData();
|
||||
data.setMainLight(state);
|
||||
data.setNightLight(false);
|
||||
data.setPreviewLight(false);
|
||||
|
||||
executeUrl("PUT", LIGHT_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setMainLightDimmer(int level) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final LightData data = new LightData();
|
||||
data.setMainLightLevel(level);
|
||||
data.setMainLight(true);
|
||||
data.setNightLight(false);
|
||||
data.setPreviewLight(false);
|
||||
|
||||
executeUrl("PUT", LIGHT_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void switchNightLight(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final LightData data = new LightData();
|
||||
data.setMainLight(false);
|
||||
data.setNightLight(state);
|
||||
data.setPreviewLight(false);
|
||||
|
||||
executeUrl("PUT", LIGHT_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void switchSunsetProgram(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final SunsetData data = new SunsetData();
|
||||
data.setState(state);
|
||||
|
||||
executeUrl("PUT", SUNSET_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setSunsetLightIntensity(int percent) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final SunsetData data = new SunsetData();
|
||||
data.setLightIntensity(percent);
|
||||
|
||||
executeUrl("PUT", SUNSET_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setSunsetDuration(int duration) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final SunsetData data = new SunsetData();
|
||||
data.setDurationInMin(duration);
|
||||
|
||||
executeUrl("PUT", SUNSET_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setSunsetColorSchema(int value) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final SunsetData data = new SunsetData();
|
||||
data.setColorSchema(value);
|
||||
|
||||
executeUrl("PUT", SUNSET_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setSunsetAmbientNoise(String option) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final SunsetData data = new SunsetData();
|
||||
data.setAmbientNoise(option);
|
||||
|
||||
executeUrl("PUT", SUNSET_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setSunsetVolume(int percent) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final SunsetData data = new SunsetData();
|
||||
data.setSoundVolume(percent);
|
||||
|
||||
executeUrl("PUT", SUNSET_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public RelaxData fetchRelaxData() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
return executeUrl("GET", RELAX_ENDPOINT, RelaxData.class);
|
||||
}
|
||||
|
||||
public void setRelaxVolume(int percent) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final RelaxData data = new RelaxData();
|
||||
data.setSoundVolume(percent);
|
||||
|
||||
executeUrl("PUT", RELAX_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setRelaxLightIntensity(int percent) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final RelaxData data = new RelaxData();
|
||||
data.setLightIntensity(percent);
|
||||
|
||||
executeUrl("PUT", RELAX_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void switchRelaxProgram(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final RelaxData data = new RelaxData();
|
||||
data.setState(state);
|
||||
|
||||
executeUrl("PUT", RELAX_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setRelaxBreathingRate(int value) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final RelaxData data = new RelaxData();
|
||||
data.setBreathingRate(value);
|
||||
|
||||
executeUrl("PUT", RELAX_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setRelaxDuration(int value) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final RelaxData data = new RelaxData();
|
||||
data.setDurationInMin(value);
|
||||
|
||||
executeUrl("PUT", RELAX_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setRelaxGuidanceType(int value) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final RelaxData data = new RelaxData();
|
||||
data.setGuidanceType(value);
|
||||
|
||||
executeUrl("PUT", RELAX_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public AudioData fetchAudioData() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
return executeUrl("GET", AUDIO_ENDPOINT, AudioData.class);
|
||||
}
|
||||
|
||||
public void switchRadio(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final AudioData data = new AudioData();
|
||||
if (state) {
|
||||
data.enableRadio();
|
||||
} else {
|
||||
data.disableAudio();
|
||||
}
|
||||
|
||||
executeUrl("PUT", AUDIO_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void switchAux(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final AudioData data = new AudioData();
|
||||
if (state) {
|
||||
data.enableAux();
|
||||
} else {
|
||||
data.disableAudio();
|
||||
}
|
||||
|
||||
executeUrl("PUT", AUDIO_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setAudioVolume(int percent) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final AudioData data = new AudioData();
|
||||
data.setVolume(percent);
|
||||
|
||||
executeUrl("PUT", AUDIO_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void setRadioChannel(String preset) throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final AudioData data = new AudioData();
|
||||
data.enableRadio();
|
||||
data.setRadioPreset(preset);
|
||||
|
||||
executeUrl("PUT", AUDIO_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public RadioData getRadioData() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
RadioData data = new RadioData();
|
||||
int loops = 0;
|
||||
do {
|
||||
if (loops > 20) {
|
||||
break;
|
||||
}
|
||||
if (loops > 0) {
|
||||
loops++;
|
||||
Thread.sleep(250);
|
||||
}
|
||||
data = executeUrl("GET", RADIO_ENDPOINT, RadioData.class);
|
||||
} while (data.isSeeking()); // Wait until seek is finished
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public void radioSeekUp() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final RadioData data = new RadioData();
|
||||
data.setCmdSeekUp();
|
||||
|
||||
executeUrl("PUT", RADIO_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public void radioSeekDown() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final RadioData data = new RadioData();
|
||||
data.setCmdSeekDown();
|
||||
|
||||
executeUrl("PUT", RADIO_ENDPOINT, data);
|
||||
}
|
||||
|
||||
public DeviceData fetchDeviceData() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
return executeUrl("GET", DEVICE_ENDPOINT, DeviceData.class);
|
||||
}
|
||||
|
||||
public WifiData fetchWifiData() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
return executeUrl("GET", WIFI_ENDPOINT, WifiData.class);
|
||||
}
|
||||
|
||||
public FirmwareData fetchFirmwareData() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
return executeUrl("GET", FIRMWARE_ENDPOINT, FirmwareData.class);
|
||||
}
|
||||
|
||||
public TimerData fetchTimerData() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
return executeUrl("GET", TIMER_ENDPOINT, TimerData.class);
|
||||
}
|
||||
|
||||
public PresetData fetchPresetData() throws TimeoutException, InterruptedException, ExecutionException {
|
||||
return executeUrl("GET", PRESET_ENDPOINT, PresetData.class);
|
||||
}
|
||||
|
||||
private <T> T executeUrl(String httpMethod, String endpoint, Class<T> classOfT)
|
||||
throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final String responseBody = executeUrl("GET", endpoint, (String) null);
|
||||
final T data = gson.fromJson(responseBody, classOfT);
|
||||
return data;
|
||||
}
|
||||
|
||||
private void executeUrl(String httpMethod, String endpoint, Object data)
|
||||
throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final String content = gson.toJson(data);
|
||||
executeUrl(httpMethod, endpoint, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given <code>url</code> with the given <code>httpMethod</code>
|
||||
*
|
||||
* @param httpMethod the HTTP method to use
|
||||
* @param endpoint the url endpoint
|
||||
* @param content the content to be sent to the given <code>url</code> or
|
||||
* <code>null</code> if no content should be sent.
|
||||
* @return
|
||||
* @throws ExecutionException
|
||||
* @throws InterruptedException
|
||||
* @throws UnsupportedEncodingException
|
||||
* @throws Exception when the request execution failed, timed out or it was interrupted
|
||||
*/
|
||||
private String executeUrl(String httpMethod, String endpoint, @Nullable String content)
|
||||
throws TimeoutException, InterruptedException, ExecutionException {
|
||||
final String url = urlBase + endpoint;
|
||||
final HttpMethod method = HttpUtil.createHttpMethod(httpMethod);
|
||||
|
||||
final Request request = httpClient.newRequest(url).method(method).timeout(REQUEST_TIMEOUT_MS,
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
||||
if (content != null && (HttpMethod.POST.equals(method) || HttpMethod.PUT.equals(method))) {
|
||||
final StringContentProvider stringContentProvider = new StringContentProvider(content,
|
||||
StandardCharsets.UTF_8);
|
||||
request.content(stringContentProvider, DEFAULT_CONTENT_TYPE);
|
||||
|
||||
logger.trace("Request for url '{}':\r\n{}", url, content);
|
||||
} else {
|
||||
logger.trace("Request for url '{}'", url);
|
||||
}
|
||||
|
||||
final ContentResponse response = request.send();
|
||||
final int statusCode = response.getStatus();
|
||||
if (logger.isDebugEnabled() && statusCode >= HttpStatus.BAD_REQUEST_400) {
|
||||
String statusLine = statusCode + " " + response.getReason();
|
||||
logger.debug("Method failed: {}", statusLine);
|
||||
}
|
||||
|
||||
final String encoding = response.getEncoding() != null ? response.getEncoding().replaceAll("\"", "").trim()
|
||||
: StandardCharsets.UTF_8.name();
|
||||
|
||||
try {
|
||||
String responseBody = new String(response.getContent(), encoding);
|
||||
logger.trace("Response for url '{}':\r\n{}", url, responseBody);
|
||||
return responseBody;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.warn("Get response content failed!", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
|
||||
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
|
||||
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
|
||||
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* Dynamic channel state description provider.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@Component(service = { DynamicStateDescriptionProvider.class, SomneoPresetStateDescriptionProvider.class })
|
||||
@NonNullByDefault
|
||||
public class SomneoPresetStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
|
||||
|
||||
@Activate
|
||||
public SomneoPresetStateDescriptionProvider(final @Reference EventPublisher eventPublisher,
|
||||
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry,
|
||||
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
||||
this.eventPublisher = eventPublisher;
|
||||
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
|
||||
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.PlayPauseType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class represents the audio state from the API.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AudioData {
|
||||
|
||||
private static final String SOURCE_RADIO = "fmr";
|
||||
|
||||
private static final String SOURCE_AUX = "aux";
|
||||
|
||||
private static final String SOURCE_OFF = "off";
|
||||
|
||||
@SerializedName("onoff")
|
||||
private @Nullable Boolean power;
|
||||
|
||||
/**
|
||||
* Must be set to false when the audio is turned on, otherwise a light that is
|
||||
* turned on will be turned off.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@SerializedName("tempy")
|
||||
private @Nullable Boolean previewLight;
|
||||
|
||||
/**
|
||||
* Volume range from 0 to 25.
|
||||
*/
|
||||
@SerializedName("sdvol")
|
||||
private @Nullable Integer volume;
|
||||
|
||||
/**
|
||||
* Current active audio source. Can be radio, aux or off.
|
||||
*/
|
||||
@SerializedName("snddv")
|
||||
private @Nullable String source;
|
||||
|
||||
/**
|
||||
* Current active radio preset.
|
||||
*/
|
||||
@SerializedName("sndch")
|
||||
private @Nullable String preset;
|
||||
|
||||
public void disableAudio() {
|
||||
power = false;
|
||||
source = SOURCE_OFF;
|
||||
}
|
||||
|
||||
public void enableRadio() {
|
||||
power = true;
|
||||
source = SOURCE_RADIO;
|
||||
previewLight = false;
|
||||
}
|
||||
|
||||
public State getRadioState() {
|
||||
final Boolean power = this.power;
|
||||
if (power == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return power && SOURCE_RADIO.equals(source) ? PlayPauseType.PLAY : PlayPauseType.PAUSE;
|
||||
}
|
||||
|
||||
public void enableAux() {
|
||||
power = true;
|
||||
source = SOURCE_AUX;
|
||||
previewLight = false;
|
||||
}
|
||||
|
||||
public State getAuxState() {
|
||||
final Boolean power = this.power;
|
||||
if (power == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return OnOffType.from(power && SOURCE_AUX.equals(source));
|
||||
}
|
||||
|
||||
public void setVolume(int percent) {
|
||||
this.volume = percent / 4;
|
||||
}
|
||||
|
||||
public State getVolumeState() {
|
||||
final Integer volume = this.volume;
|
||||
if (volume == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new PercentType(volume * 4);
|
||||
}
|
||||
|
||||
public void setRadioPreset(String preset) {
|
||||
this.preset = preset;
|
||||
}
|
||||
|
||||
public State getPresetState() {
|
||||
final String preset = this.preset;
|
||||
if (preset == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new StringType(preset);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class represents the device data from the API.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DeviceData {
|
||||
|
||||
@SerializedName("type")
|
||||
private @Nullable String modelId;
|
||||
|
||||
@SerializedName("serial")
|
||||
private @Nullable String serial;
|
||||
|
||||
public @Nullable String getModelId() {
|
||||
return modelId;
|
||||
}
|
||||
|
||||
public @Nullable String getSerial() {
|
||||
return serial;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class represents the firmware data from the API.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FirmwareData {
|
||||
|
||||
@SerializedName("version")
|
||||
private @Nullable String version;
|
||||
|
||||
public @Nullable String getVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class represents the light state from the API.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LightData {
|
||||
|
||||
/**
|
||||
* Brightness range from 0 to 25.
|
||||
*/
|
||||
@SerializedName("ltlvl")
|
||||
private @Nullable Integer mainLightLevel;
|
||||
|
||||
@SerializedName("onoff")
|
||||
private @Nullable Boolean mainLight;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@SerializedName("tempy")
|
||||
private @Nullable Boolean previewLight;
|
||||
|
||||
@SerializedName("ngtlt")
|
||||
private @Nullable Boolean nightLight;
|
||||
|
||||
public int getMainLightLevel() {
|
||||
final Integer mainLightLevel = this.mainLightLevel;
|
||||
if (mainLightLevel == null) {
|
||||
return 0;
|
||||
}
|
||||
return mainLightLevel * 4;
|
||||
}
|
||||
|
||||
public void setMainLightLevel(int mainLightLevel) {
|
||||
this.mainLightLevel = mainLightLevel / 4;
|
||||
}
|
||||
|
||||
public State getMainLightState() {
|
||||
final Boolean mainLight = this.mainLight;
|
||||
final Integer mainLightLevel = this.mainLightLevel;
|
||||
if (mainLight == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
if (mainLightLevel == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
if (mainLight) {
|
||||
return new PercentType(mainLightLevel * 4);
|
||||
}
|
||||
return OnOffType.OFF;
|
||||
}
|
||||
|
||||
public void setMainLight(boolean mainLight) {
|
||||
this.mainLight = mainLight;
|
||||
}
|
||||
|
||||
public void setPreviewLight(boolean previewLight) {
|
||||
this.previewLight = previewLight;
|
||||
}
|
||||
|
||||
public State getNightLightState() {
|
||||
final Boolean nightLight = this.nightLight;
|
||||
if (nightLight == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return OnOffType.from(nightLight);
|
||||
}
|
||||
|
||||
public void setNightLight(@Nullable Boolean nightLight) {
|
||||
this.nightLight = nightLight;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.types.StateOption;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class represents the preset state from the API.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PresetData {
|
||||
|
||||
private static final String LABEL_TEMPLATE = "%s fm";
|
||||
private static final String LABEL_EMPTY_TEMPLATE = "FM %s";
|
||||
|
||||
@SerializedName("1")
|
||||
private @Nullable String preset1;
|
||||
|
||||
@SerializedName("2")
|
||||
private @Nullable String preset2;
|
||||
|
||||
@SerializedName("3")
|
||||
private @Nullable String preset3;
|
||||
|
||||
@SerializedName("4")
|
||||
private @Nullable String preset4;
|
||||
|
||||
@SerializedName("5")
|
||||
private @Nullable String preset5;
|
||||
|
||||
public List<StateOption> createPresetOptions() {
|
||||
List<StateOption> stateOptions = new ArrayList<>();
|
||||
stateOptions.add(createStateOption("1", preset1));
|
||||
stateOptions.add(createStateOption("2", preset2));
|
||||
stateOptions.add(createStateOption("3", preset3));
|
||||
stateOptions.add(createStateOption("4", preset4));
|
||||
stateOptions.add(createStateOption("5", preset5));
|
||||
return stateOptions;
|
||||
}
|
||||
|
||||
private static StateOption createStateOption(String index, @Nullable String preset) {
|
||||
String label;
|
||||
if (preset == null || "".equals(preset)) {
|
||||
label = String.format(LABEL_EMPTY_TEMPLATE, index);
|
||||
} else {
|
||||
label = String.format(LABEL_TEMPLATE, preset);
|
||||
}
|
||||
return new StateOption(index, label);
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class represents the radio state from the API.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RadioData {
|
||||
|
||||
private static final String LABEL_TEMPLATE = "%s fm";
|
||||
|
||||
private static final String CMD_SEEK_UP = "seekup";
|
||||
|
||||
private static final String CMD_SEEK_DOWN = "seekdown";
|
||||
|
||||
@SerializedName("fmfrq")
|
||||
private @Nullable String frequency;
|
||||
|
||||
@SerializedName("fmcmd")
|
||||
private @Nullable String command;
|
||||
|
||||
public State getFrequency() {
|
||||
final String frequency = this.frequency;
|
||||
if (frequency == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new StringType(String.format(LABEL_TEMPLATE, frequency));
|
||||
}
|
||||
|
||||
public void setCmdSeekUp() {
|
||||
this.command = CMD_SEEK_UP;
|
||||
}
|
||||
|
||||
public void setCmdSeekDown() {
|
||||
this.command = CMD_SEEK_DOWN;
|
||||
}
|
||||
|
||||
public boolean isSeeking() {
|
||||
return CMD_SEEK_UP.equals(command) || CMD_SEEK_DOWN.equals(command);
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class represents the relax program state from the API.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RelaxData {
|
||||
|
||||
@SerializedName("onoff")
|
||||
private @Nullable Boolean state;
|
||||
|
||||
@SerializedName("progr")
|
||||
private @Nullable Integer breathingRate;
|
||||
|
||||
@SerializedName("durat")
|
||||
private @Nullable Integer durationInMin;
|
||||
|
||||
@SerializedName("rtype")
|
||||
private @Nullable Integer guidanceType;
|
||||
|
||||
/**
|
||||
* Brightness range from 0 to 25.
|
||||
*/
|
||||
@SerializedName("intny")
|
||||
private @Nullable Integer lightIntensity;
|
||||
|
||||
/**
|
||||
* Volume range from 0 to 25.
|
||||
*/
|
||||
@SerializedName("sndlv")
|
||||
private @Nullable Integer soundVolume;
|
||||
|
||||
public State getSwitchState() {
|
||||
final Boolean state = this.state;
|
||||
if (state == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return OnOffType.from(state);
|
||||
}
|
||||
|
||||
public void setState(boolean state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public State getBreathingRate() {
|
||||
final Integer breathingRate = this.breathingRate;
|
||||
if (breathingRate == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new DecimalType(breathingRate);
|
||||
}
|
||||
|
||||
public void setBreathingRate(int breathingRate) {
|
||||
this.breathingRate = breathingRate;
|
||||
}
|
||||
|
||||
public State getDurationInMin() {
|
||||
final Integer durationInMin = this.durationInMin;
|
||||
if (durationInMin == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new DecimalType(durationInMin);
|
||||
}
|
||||
|
||||
public void setDurationInMin(int durationInMin) {
|
||||
this.durationInMin = durationInMin;
|
||||
}
|
||||
|
||||
public State getGuidanceType() {
|
||||
final Integer guidanceType = this.guidanceType;
|
||||
if (guidanceType == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new DecimalType(guidanceType);
|
||||
}
|
||||
|
||||
public void setGuidanceType(int guidanceType) {
|
||||
this.guidanceType = guidanceType;
|
||||
}
|
||||
|
||||
public State getLightIntensity() {
|
||||
final Integer lightIntensity = this.lightIntensity;
|
||||
if (lightIntensity == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new PercentType(lightIntensity * 4);
|
||||
}
|
||||
|
||||
public void setLightIntensity(int percent) {
|
||||
this.lightIntensity = percent / 4;
|
||||
}
|
||||
|
||||
public State getSoundVolume() {
|
||||
final Integer soundVolume = this.soundVolume;
|
||||
if (soundVolume == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new PercentType(soundVolume * 4);
|
||||
}
|
||||
|
||||
public void setSoundVolume(int percent) {
|
||||
this.soundVolume = percent / 4;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
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.types.State;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class represents the sensor state from the API.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SensorData {
|
||||
|
||||
@SerializedName("mslux")
|
||||
private float currentIlluminance;
|
||||
|
||||
@SerializedName("mstmp")
|
||||
private float currentTemperature;
|
||||
|
||||
@SerializedName("msrhu")
|
||||
private float currentHumidity;
|
||||
|
||||
@SerializedName("mssnd")
|
||||
private int currentNoise;
|
||||
|
||||
public State getCurrentIlluminance() {
|
||||
return new QuantityType<>(currentIlluminance, Units.LUX);
|
||||
}
|
||||
|
||||
public State getCurrentTemperature() {
|
||||
return new QuantityType<>(currentTemperature, SIUnits.CELSIUS);
|
||||
}
|
||||
|
||||
public State getCurrentHumidity() {
|
||||
return new QuantityType<>(currentHumidity, Units.PERCENT);
|
||||
}
|
||||
|
||||
public State getCurrentNoise() {
|
||||
return new QuantityType<>(currentNoise, Units.DECIBEL);
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class represents the sunset program state from the API.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SunsetData {
|
||||
|
||||
@SerializedName("onoff")
|
||||
private @Nullable Boolean state;
|
||||
|
||||
/**
|
||||
* Brightness range from 0 to 25.
|
||||
*/
|
||||
@SerializedName("curve")
|
||||
private @Nullable Integer lightIntensity;
|
||||
|
||||
@SerializedName("durat")
|
||||
private @Nullable Integer durationInMin;
|
||||
|
||||
@SerializedName("ctype")
|
||||
private @Nullable Integer colorSchema;
|
||||
|
||||
@SerializedName("snddv")
|
||||
private @Nullable String soundSource;
|
||||
|
||||
@SerializedName("sndch")
|
||||
private @Nullable String ambientNoise;
|
||||
|
||||
/**
|
||||
* Volume range from 0 to 25.
|
||||
*/
|
||||
@SerializedName("sndlv")
|
||||
private @Nullable Integer soundVolume;
|
||||
|
||||
public State getSwitchState() {
|
||||
final Boolean state = this.state;
|
||||
if (state == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return OnOffType.from(state);
|
||||
}
|
||||
|
||||
public void setState(boolean state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public State getLightIntensity() {
|
||||
final Integer lightIntensity = this.lightIntensity;
|
||||
if (lightIntensity == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new PercentType(lightIntensity * 4);
|
||||
}
|
||||
|
||||
public void setLightIntensity(int percent) {
|
||||
this.lightIntensity = percent / 4;
|
||||
}
|
||||
|
||||
public State getDurationInMin() {
|
||||
final Integer durationInMin = this.durationInMin;
|
||||
if (durationInMin == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new DecimalType(durationInMin);
|
||||
}
|
||||
|
||||
public void setDurationInMin(int durationInMin) {
|
||||
this.durationInMin = durationInMin;
|
||||
}
|
||||
|
||||
public State getColorSchema() {
|
||||
final Integer colorSchema = this.colorSchema;
|
||||
if (colorSchema == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new DecimalType(colorSchema);
|
||||
}
|
||||
|
||||
public void setColorSchema(int colorSchema) {
|
||||
this.colorSchema = colorSchema;
|
||||
}
|
||||
|
||||
public State getAmbientNoise() {
|
||||
final String soundSource = this.soundSource;
|
||||
if (soundSource == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
final String suffix = "off".equals(soundSource) ? "" : "-" + ambientNoise;
|
||||
return new StringType(soundSource + suffix);
|
||||
}
|
||||
|
||||
public void setAmbientNoise(String option) {
|
||||
final String[] values = option.split("-");
|
||||
soundSource = values[0];
|
||||
ambientNoise = values.length == 1 ? "" : values[1];
|
||||
}
|
||||
|
||||
public State getSoundVolume() {
|
||||
final Integer soundVolume = this.soundVolume;
|
||||
if (soundVolume == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
return new PercentType(soundVolume * 4);
|
||||
}
|
||||
|
||||
public void setSoundVolume(int percent) {
|
||||
this.soundVolume = percent / 4;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class represents the program timer state from the API.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TimerData {
|
||||
|
||||
@SerializedName("rlxmn")
|
||||
private int relaxMinutes;
|
||||
|
||||
@SerializedName("rlxsc")
|
||||
private int relaxSeconds;
|
||||
|
||||
@SerializedName("dskmn")
|
||||
private int sunsetMinutes;
|
||||
|
||||
@SerializedName("dsksc")
|
||||
private int sunsetSeconds;
|
||||
|
||||
public int remainingTimeRelax() {
|
||||
return relaxMinutes * 60 + relaxSeconds;
|
||||
}
|
||||
|
||||
public int remainingTimeSunset() {
|
||||
return sunsetMinutes * 60 + sunsetSeconds;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.somneo.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class represents the wifi data from the API.
|
||||
*
|
||||
* @author Michael Myrcik - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WifiData {
|
||||
|
||||
@SerializedName("macaddress")
|
||||
private @Nullable String macAddress;
|
||||
|
||||
public @Nullable String getMacAddress() {
|
||||
return macAddress;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="somneo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>Somneo Binding</name>
|
||||
<description>This is the binding for Philips Somneo devices.</description>
|
||||
|
||||
</binding:binding>
|
@ -0,0 +1,108 @@
|
||||
# binding
|
||||
binding.somneo.name = Somneo Binding
|
||||
binding.somneo.description = This is the binding for Philips Somneo devices.
|
||||
|
||||
# thing types
|
||||
thing-type.somneo.hf367x.label = Philips Somneo Light
|
||||
thing-type.somneo.hf367x.description = A smart sleep and wake-up light with sensors.
|
||||
|
||||
# thing type config description
|
||||
thing-type.config.somneo.hf367x.hostname.label = Hostname
|
||||
thing-type.config.somneo.hf367x.hostname.description = Hostname or IP address of the device.
|
||||
thing-type.config.somneo.hf367x.ignoreSSLErrors.label = Ignore SSL Errors
|
||||
thing-type.config.somneo.hf367x.ignoreSSLErrors.description = If set to true ignores invalid SSL certificate errors. This is potentially dangerous.
|
||||
thing-type.config.somneo.hf367x.port.label = HTTP Port
|
||||
thing-type.config.somneo.hf367x.port.description = HTTP Port used for communication. Normally shouldn't be changed.
|
||||
thing-type.config.somneo.hf367x.refreshInterval.label = Refresh Interval
|
||||
thing-type.config.somneo.hf367x.refreshInterval.description = Interval the device is polled in sec.
|
||||
|
||||
# channel group types
|
||||
channel-group-type.somneo.audio.label = Audio Player
|
||||
channel-group-type.somneo.audio.description = Channels to control the audio player.
|
||||
channel-group-type.somneo.audio.channel.aux.label = AUX Input
|
||||
channel-group-type.somneo.audio.channel.aux.description = Turn the AUX input on or off.
|
||||
channel-group-type.somneo.audio.channel.radio.label = Radio Remote Control
|
||||
channel-group-type.somneo.audio.channel.radio.description = Remote control for controlling the radio and seeking for a frequency.
|
||||
channel-group-type.somneo.light.label = Light
|
||||
channel-group-type.somneo.light.description = Different light channels.
|
||||
channel-group-type.somneo.sensor.label = Sensor Data
|
||||
channel-group-type.somneo.sensor.description = Data from the various sensors.
|
||||
channel-group-type.somneo.relax.label = Relax Breathe
|
||||
channel-group-type.somneo.relax.description = Light-guided breathing helps you relax for sleep.
|
||||
channel-group-type.somneo.relax.channel.lightIntensity.label = Light Intensity
|
||||
channel-group-type.somneo.relax.channel.lightIntensity.description = The channel allows to set the light intensity.
|
||||
channel-group-type.somneo.relax.channel.switch.label = Relax Breathe Program
|
||||
channel-group-type.somneo.relax.channel.switch.description = The switch channel allows to turn the relax breathe program on or off.
|
||||
channel-group-type.somneo.relax.channel.volume.label = Volume
|
||||
channel-group-type.somneo.relax.channel.volume.description = Set the volume of the relax breath program.
|
||||
channel-group-type.somneo.sunset.label = Sunset
|
||||
channel-group-type.somneo.sunset.description = Simulate a sunset with selectable lights and sounds.
|
||||
channel-group-type.somneo.sunset.channel.lightIntensity.label = Light Intensity
|
||||
channel-group-type.somneo.sunset.channel.lightIntensity.description = The channel allows to set the light intensity.
|
||||
channel-group-type.somneo.sunset.channel.switch.label = Sunset Program
|
||||
channel-group-type.somneo.sunset.channel.switch.description = The switch channel allows to turn the sunset program on or off.
|
||||
channel-group-type.somneo.sunset.channel.volume.label = Volume
|
||||
channel-group-type.somneo.sunset.channel.volume.description = Set the volume of the sunset program.
|
||||
|
||||
# channel types
|
||||
channel-type.somneo.ambientNoise.label = Ambient Noise
|
||||
channel-type.somneo.ambientNoise.description = Ambient noise played during the sunset.
|
||||
channel-type.somneo.ambientNoise.state.option.off = No sound
|
||||
channel-type.somneo.ambientNoise.state.option.dus-1 = Soft Rain
|
||||
channel-type.somneo.ambientNoise.state.option.dus-2 = Ocean Waves
|
||||
channel-type.somneo.ambientNoise.state.option.dus-3 = Under Water
|
||||
channel-type.somneo.ambientNoise.state.option.dus-4 = Summer Lake
|
||||
channel-type.somneo.ambientNoise.state.option.fmr-1 = FM 1
|
||||
channel-type.somneo.ambientNoise.state.option.fmr-2 = FM 2
|
||||
channel-type.somneo.ambientNoise.state.option.fmr-3 = FM 3
|
||||
channel-type.somneo.ambientNoise.state.option.fmr-4 = FM 4
|
||||
channel-type.somneo.ambientNoise.state.option.fmr-5 = FM 5
|
||||
channel-type.somneo.breathingRate.label = Breathing Rate
|
||||
channel-type.somneo.breathingRate.description = Breathing rate per minute.
|
||||
channel-type.somneo.breathingRate.state.option.1 = 4 BR/min
|
||||
channel-type.somneo.breathingRate.state.option.2 = 5 BR/min
|
||||
channel-type.somneo.breathingRate.state.option.3 = 6 BR/min
|
||||
channel-type.somneo.breathingRate.state.option.4 = 7 BR/min
|
||||
channel-type.somneo.breathingRate.state.option.5 = 8 BR/min
|
||||
channel-type.somneo.breathingRate.state.option.6 = 9 BR/min
|
||||
channel-type.somneo.breathingRate.state.option.7 = 10 BR/min
|
||||
channel-type.somneo.colorSchema.label = Color Schema
|
||||
channel-type.somneo.colorSchema.description = Choose a personal sunset.
|
||||
channel-type.somneo.colorSchema.state.option.0 = Sunny day
|
||||
channel-type.somneo.colorSchema.state.option.1 = Island red
|
||||
channel-type.somneo.colorSchema.state.option.2 = Nordic white
|
||||
channel-type.somneo.colorSchema.state.option.3 = Caribbean red
|
||||
channel-type.somneo.frequency.label = Frequency
|
||||
channel-type.somneo.frequency.description = The currently selected radio frequency.
|
||||
channel-type.somneo.guidanceType.label = Breath Guidance Type
|
||||
channel-type.somneo.guidanceType.description = Select a breath guidance type.
|
||||
channel-type.somneo.guidanceType.state.option.0 = Light
|
||||
channel-type.somneo.guidanceType.state.option.1 = Sound
|
||||
channel-type.somneo.humidity.label = Humidity
|
||||
channel-type.somneo.humidity.description = The current humidity in %.
|
||||
channel-type.somneo.illuminance.label = Illuminance
|
||||
channel-type.somneo.illuminance.description = The current illuminance in lux.
|
||||
channel-type.somneo.light.label = Night Light
|
||||
channel-type.somneo.light.description = The light switch channel allows to turn the night light on or off.
|
||||
channel-type.somneo.noise.label = Noise
|
||||
channel-type.somneo.noise.description = The current noise in dB.
|
||||
channel-type.somneo.preset.label = FM Preset
|
||||
channel-type.somneo.preset.description = The Device has 5 presets to store radio frequencies.
|
||||
channel-type.somneo.relaxDuration.label = Duration
|
||||
channel-type.somneo.relaxDuration.description = The duration of relax breathe program in minutes.
|
||||
channel-type.somneo.relaxDuration.state.option.5 = 5 Minutes
|
||||
channel-type.somneo.relaxDuration.state.option.10 = 10 Minutes
|
||||
channel-type.somneo.relaxDuration.state.option.15 = 15 Minutes
|
||||
channel-type.somneo.remainingTime.label = Remaining Time
|
||||
channel-type.somneo.remainingTime.description = Remaining time from an activated program.
|
||||
channel-type.somneo.sunsetDuration.label = Duration
|
||||
channel-type.somneo.sunsetDuration.description = The duration of sunset program in minutes.
|
||||
channel-type.somneo.sunsetDuration.state.option.5 = 5 Minutes
|
||||
channel-type.somneo.sunsetDuration.state.option.10 = 10 Minutes
|
||||
channel-type.somneo.sunsetDuration.state.option.15 = 15 Minutes
|
||||
channel-type.somneo.sunsetDuration.state.option.20 = 20 Minutes
|
||||
channel-type.somneo.sunsetDuration.state.option.30 = 30 Minutes
|
||||
channel-type.somneo.sunsetDuration.state.option.45 = 45 Minutes
|
||||
channel-type.somneo.sunsetDuration.state.option.60 = 60 Minutes
|
||||
channel-type.somneo.temperature.label = Temperature
|
||||
channel-type.somneo.temperature.description = The current temperature in °C.
|
@ -0,0 +1,108 @@
|
||||
# binding
|
||||
binding.somneo.name = Somneo Binding
|
||||
binding.somneo.description = Dieses Binding integriert Philips Somneo Geräte.
|
||||
|
||||
# thing types
|
||||
thing-type.somneo.hf367x.label = Philips Somneo Light
|
||||
thing-type.somneo.hf367x.description = Ein intelligentes Schlaf- und Wecklicht mit Sensoren.
|
||||
|
||||
# thing type config description
|
||||
thing-type.config.somneo.hf367x.hostname.label = Hostname
|
||||
thing-type.config.somneo.hf367x.hostname.description = Hostname oder IP-Adresse des Geräts.
|
||||
thing-type.config.somneo.hf367x.ignoreSSLErrors.label = SSL-Fehler ignorieren
|
||||
thing-type.config.somneo.hf367x.ignoreSSLErrors.description = Bei der Einstellung true werden Fehler bei ungültigen SSL-Zertifikaten ignoriert. Dies ist potenziell gefährlich.
|
||||
thing-type.config.somneo.hf367x.port.label = HTTP-Port
|
||||
thing-type.config.somneo.hf367x.port.description = HTTP-Port für Kommunikation. Sollte normalerweise nicht geändert werden.
|
||||
thing-type.config.somneo.hf367x.refreshInterval.label = Aktualisierungsintervall
|
||||
thing-type.config.somneo.hf367x.refreshInterval.description = Intervall in Sekunden, in dem das Gerät abgefragt wird.
|
||||
|
||||
# channel group types
|
||||
channel-group-type.somneo.audio.label = Audioplayer
|
||||
channel-group-type.somneo.audio.description = Kanäle zur Steuerung des Audioplayers.
|
||||
channel-group-type.somneo.audio.channel.aux.label = AUX-Eingang
|
||||
channel-group-type.somneo.audio.channel.aux.description = Schaltet den AUX-Eingang ein oder aus.
|
||||
channel-group-type.somneo.audio.channel.radio.label = Radio Fernbedienung
|
||||
channel-group-type.somneo.audio.channel.radio.description = Fernbedienung zur Steuerung des Radios und zur Frequenzsuche.
|
||||
channel-group-type.somneo.light.label = Licht
|
||||
channel-group-type.somneo.light.description = Verschiedene Lichtkanäle.
|
||||
channel-group-type.somneo.sensor.label = Sensor-Daten
|
||||
channel-group-type.somneo.sensor.description = Daten von den verschiedenen Sensoren.
|
||||
channel-group-type.somneo.relax.label = Relax Breathe
|
||||
channel-group-type.somneo.relax.description = Pulsierende Lichteffekte entspannen die Atmung und unterstützen das Einschlafen.
|
||||
channel-group-type.somneo.relax.channel.lightIntensity.label = Lichtintensität
|
||||
channel-group-type.somneo.relax.channel.lightIntensity.description = Der Kanal ermöglicht die Einstellung der Lichtintensität.
|
||||
channel-group-type.somneo.relax.channel.switch.label = Relax Breathe Programm
|
||||
channel-group-type.somneo.relax.channel.switch.description = Mit dem Schaltkanal kann das Relax Breathe Programm ein- oder ausschalten werden.
|
||||
channel-group-type.somneo.relax.channel.volume.label = Lautstärke
|
||||
channel-group-type.somneo.relax.channel.volume.description = Stellen Sie die Lautstärke des Relax Breathe Programm ein.
|
||||
channel-group-type.somneo.sunset.label = Sonnenuntergang
|
||||
channel-group-type.somneo.sunset.description = Simulieren einen Sonnenuntergang mit wählbaren Lichtern und Geräuschen.
|
||||
channel-group-type.somneo.sunset.channel.lightIntensity.label = Lichtintensität
|
||||
channel-group-type.somneo.sunset.channel.lightIntensity.description = Der Kanal ermöglicht die Einstellung der Lichtintensität.
|
||||
channel-group-type.somneo.sunset.channel.switch.label = Sonnenuntergang Programm
|
||||
channel-group-type.somneo.sunset.channel.switch.description = Mit dem Schaltkanal kann das Sonnenuntergangsprogramm ein- oder ausschalten werden.
|
||||
channel-group-type.somneo.sunset.channel.volume.label = Lautstärke
|
||||
channel-group-type.somneo.sunset.channel.volume.description = Stellen Sie die Lautstärke des Sonnenuntergangsprogramms ein.
|
||||
|
||||
# channel types
|
||||
channel-type.somneo.ambientNoise.label = Umgebungsgeräusche
|
||||
channel-type.somneo.ambientNoise.description = Umgebungsgeräusche, die während des Sonnenuntergangs abgespielt werden.
|
||||
channel-type.somneo.ambientNoise.state.option.off = Kein Geräusch
|
||||
channel-type.somneo.ambientNoise.state.option.dus-1 = Soft Rain
|
||||
channel-type.somneo.ambientNoise.state.option.dus-2 = Ocean Waves
|
||||
channel-type.somneo.ambientNoise.state.option.dus-3 = Under Water
|
||||
channel-type.somneo.ambientNoise.state.option.dus-4 = Summer Lake
|
||||
channel-type.somneo.ambientNoise.state.option.fmr-1 = FM 1
|
||||
channel-type.somneo.ambientNoise.state.option.fmr-2 = FM 2
|
||||
channel-type.somneo.ambientNoise.state.option.fmr-3 = FM 3
|
||||
channel-type.somneo.ambientNoise.state.option.fmr-4 = FM 4
|
||||
channel-type.somneo.ambientNoise.state.option.fmr-5 = FM 5
|
||||
channel-type.somneo.breathingRate.label = Atemzüge
|
||||
channel-type.somneo.breathingRate.description = Atemzüge pro Minute.
|
||||
channel-type.somneo.breathingRate.state.option.1 = 4 AZ/min
|
||||
channel-type.somneo.breathingRate.state.option.2 = 5 AZ/min
|
||||
channel-type.somneo.breathingRate.state.option.3 = 6 AZ/min
|
||||
channel-type.somneo.breathingRate.state.option.4 = 7 AZ/min
|
||||
channel-type.somneo.breathingRate.state.option.5 = 8 AZ/min
|
||||
channel-type.somneo.breathingRate.state.option.6 = 9 AZ/min
|
||||
channel-type.somneo.breathingRate.state.option.7 = 10 AZ/min
|
||||
channel-type.somneo.colorSchema.label = Farbschema
|
||||
channel-type.somneo.colorSchema.description = Wählen einen persönlichen Sonnenuntergang.
|
||||
channel-type.somneo.colorSchema.state.option.0 = Sunny day
|
||||
channel-type.somneo.colorSchema.state.option.1 = Island red
|
||||
channel-type.somneo.colorSchema.state.option.2 = Nordic white
|
||||
channel-type.somneo.colorSchema.state.option.3 = Caribbean red
|
||||
channel-type.somneo.frequency.label = Frequenz
|
||||
channel-type.somneo.frequency.description = Die aktuell gewählte Frequenz.
|
||||
channel-type.somneo.guidanceType.label = Atemzugführung Art
|
||||
channel-type.somneo.guidanceType.description = Wähle einen Art der Atemzuführung aus.
|
||||
channel-type.somneo.guidanceType.state.option.0 = Licht
|
||||
channel-type.somneo.guidanceType.state.option.1 = Sound
|
||||
channel-type.somneo.humidity.label = Luftfeuchtigkeit
|
||||
channel-type.somneo.humidity.description = Die aktuelle Luftfeuchtigkeit in %.
|
||||
channel-type.somneo.illuminance.label = Beleuchtungsstärke
|
||||
channel-type.somneo.illuminance.description = Die aktuelle Beleuchtungsstärke in Lux.
|
||||
channel-type.somneo.light.label = Nachtlicht
|
||||
channel-type.somneo.light.description = Mit dem Lichtschalterkanal kann das Nachtlicht ein- und ausgeschaltet werden.
|
||||
channel-type.somneo.noise.label = Lärm
|
||||
channel-type.somneo.noise.description = Der aktuelle Lärm in dB.
|
||||
channel-type.somneo.preset.label = FM-Voreinstellung
|
||||
channel-type.somneo.preset.description = Das Gerät verfügt über 5 Voreinstellung zum Speichern von Frequenzen.
|
||||
channel-type.somneo.relaxDuration.label = Dauer
|
||||
channel-type.somneo.relaxDuration.description = Die Dauer des Relax Breathe Programm in Minuten.
|
||||
channel-type.somneo.relaxDuration.state.option.5 = 5 Minuten
|
||||
channel-type.somneo.relaxDuration.state.option.10 = 10 Minuten
|
||||
channel-type.somneo.relaxDuration.state.option.15 = 15 Minuten
|
||||
channel-type.somneo.remainingTime.label = Verbleibende Zeit
|
||||
channel-type.somneo.remainingTime.description = Verbleibende Zeit eines aktivierten Programms.
|
||||
channel-type.somneo.sunsetDuration.label = Dauer
|
||||
channel-type.somneo.sunsetDuration.description = Die Dauer des Sonnenuntergangsprogramms in Minuten.
|
||||
channel-type.somneo.sunsetDuration.state.option.5 = 5 Minuten
|
||||
channel-type.somneo.sunsetDuration.state.option.10 = 10 Minuten
|
||||
channel-type.somneo.sunsetDuration.state.option.15 = 15 Minuten
|
||||
channel-type.somneo.sunsetDuration.state.option.20 = 20 Minuten
|
||||
channel-type.somneo.sunsetDuration.state.option.30 = 30 Minuten
|
||||
channel-type.somneo.sunsetDuration.state.option.45 = 45 Minuten
|
||||
channel-type.somneo.sunsetDuration.state.option.60 = 60 Minuten
|
||||
channel-type.somneo.temperature.label = Temperatur
|
||||
channel-type.somneo.temperature.description = Die aktuelle Temperatur in °C.
|
@ -0,0 +1,338 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="somneo"
|
||||
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="hf367x">
|
||||
<label>Philips Somneo Light</label>
|
||||
<description>A smart sleep and wake-up light with sensors.</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="sensor" typeId="sensor"/>
|
||||
<channel-group id="light" typeId="light"/>
|
||||
<channel-group id="sunset" typeId="sunset"/>
|
||||
<channel-group id="relax" typeId="relax"/>
|
||||
<channel-group id="audio" typeId="audio"/>
|
||||
</channel-groups>
|
||||
|
||||
<config-description>
|
||||
<parameter name="hostname" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
<label>Hostname</label>
|
||||
<description>Hostname or IP address of the device.</description>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer">
|
||||
<label>HTTP Port</label>
|
||||
<description>HTTP Port used for communication. Normally shouldn't be changed.</description>
|
||||
<default>443</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer" unit="s" min="1">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Interval the device is polled in sec.</description>
|
||||
<default>30</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="ignoreSSLErrors" type="boolean">
|
||||
<label>Ignore SSL Errors</label>
|
||||
<description>If set to true ignores invalid SSL certificate errors. This is potentially dangerous.</description>
|
||||
<default>true</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="audio">
|
||||
<label>Audio Player</label>
|
||||
<description>Channels to control the audio player.</description>
|
||||
<category>Player</category>
|
||||
<channels>
|
||||
<channel id="radio" typeId="system.media-control">
|
||||
<label>Radio Remote Control</label>
|
||||
<description>Remote control for controlling the radio and seeking for a frequency.</description>
|
||||
</channel>
|
||||
<channel id="aux" typeId="system.power">
|
||||
<label>AUX Input</label>
|
||||
<description>Turn the AUX input on or off.</description>
|
||||
</channel>
|
||||
<channel id="volume" typeId="system.volume"/>
|
||||
<channel id="preset" typeId="preset"/>
|
||||
<channel id="frequency" typeId="frequency"></channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="light">
|
||||
<label>Light</label>
|
||||
<description>Different light channels.</description>
|
||||
<category>Light</category>
|
||||
<channels>
|
||||
<channel id="main" typeId="system.brightness"/>
|
||||
<channel id="night" typeId="light"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="sensor">
|
||||
<label>Sensor Data</label>
|
||||
<description>Data from the various sensors.</description>
|
||||
<category>Sensor</category>
|
||||
<channels>
|
||||
<channel id="illuminance" typeId="illuminance"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="humidity" typeId="humidity"/>
|
||||
<channel id="noise" typeId="noise"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="relax">
|
||||
<label>Relax Breathe</label>
|
||||
<description>Light-guided breathing helps you relax for sleep.</description>
|
||||
<channels>
|
||||
<channel id="switch" typeId="system.power">
|
||||
<label>Relax Breathe Program</label>
|
||||
<description>The switch channel allows to turn the relax breathe program on or off.</description>
|
||||
</channel>
|
||||
<channel id="remainingTime" typeId="remainingTime"/>
|
||||
<channel id="breathingRate" typeId="breathingRate"/>
|
||||
<channel id="duration" typeId="relaxDuration"/>
|
||||
<channel id="guidanceType" typeId="guidanceType"/>
|
||||
<channel id="lightIntensity" typeId="system.brightness">
|
||||
<label>Light Intensity</label>
|
||||
<description>The channel allows to set the light intensity.</description>
|
||||
</channel>
|
||||
<channel id="volume" typeId="system.volume"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="sunset">
|
||||
<label>Sunset</label>
|
||||
<description>Simulate a sunset with selectable lights and sounds.</description>
|
||||
<category>Sunset</category>
|
||||
<channels>
|
||||
<channel id="switch" typeId="system.power">
|
||||
<label>Sunset Program</label>
|
||||
<description>The switch channel allows to turn the sunset program on or off.</description>
|
||||
</channel>
|
||||
<channel id="remainingTime" typeId="remainingTime"/>
|
||||
<channel id="lightIntensity" typeId="system.brightness">
|
||||
<label>Light Intensity</label>
|
||||
<description>The channel allows to set the light intensity.</description>
|
||||
</channel>
|
||||
<channel id="duration" typeId="sunsetDuration"/>
|
||||
<channel id="colorSchema" typeId="colorSchema"/>
|
||||
<channel id="ambientNoise" typeId="ambientNoise"/>
|
||||
<channel id="volume" typeId="system.volume"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="ambientNoise">
|
||||
<item-type>String</item-type>
|
||||
<label>Ambient Noise</label>
|
||||
<description>Ambient noise played during the sunset.</description>
|
||||
<category>Noise</category>
|
||||
<tags>
|
||||
<tag>Control</tag>
|
||||
<tag>Noise</tag>
|
||||
</tags>
|
||||
<state>
|
||||
<options>
|
||||
<option value="off">No sound</option>
|
||||
<option value="dus-1">Soft Rain</option>
|
||||
<option value="dus-2">Ocean Waves</option>
|
||||
<option value="dus-3">Under Water</option>
|
||||
<option value="dus-4">Summer Lake</option>
|
||||
<option value="fmr-1">FM 1</option>
|
||||
<option value="fmr-2">FM 2</option>
|
||||
<option value="fmr-3">FM 3</option>
|
||||
<option value="fmr-4">FM 4</option>
|
||||
<option value="fmr-5">FM 5</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="breathingRate">
|
||||
<item-type>Number</item-type>
|
||||
<label>Breathing Rate</label>
|
||||
<description>Breathing rate per minute.</description>
|
||||
<tags>
|
||||
<tag>Control</tag>
|
||||
</tags>
|
||||
<state>
|
||||
<options>
|
||||
<option value="1">4 BR/min</option>
|
||||
<option value="2">5 BR/min</option>
|
||||
<option value="3">6 BR/min</option>
|
||||
<option value="4">7 BR/min</option>
|
||||
<option value="5">8 BR/min</option>
|
||||
<option value="6">9 BR/min</option>
|
||||
<option value="7">10 BR/min</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="colorSchema">
|
||||
<item-type>Number</item-type>
|
||||
<label>Color Schema</label>
|
||||
<description>Choose a personal sunset.</description>
|
||||
<category>Sunset</category>
|
||||
<tags>
|
||||
<tag>Control</tag>
|
||||
<tag>ColorTemperature</tag>
|
||||
</tags>
|
||||
<state>
|
||||
<options>
|
||||
<option value="0">Sunny day</option>
|
||||
<option value="1">Island red</option>
|
||||
<option value="2">Nordic white</option>
|
||||
<option value="3">Caribbean red</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="frequency">
|
||||
<item-type>String</item-type>
|
||||
<label>Frequency</label>
|
||||
<description>The currently selected radio frequency.</description>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="guidanceType">
|
||||
<item-type>Number</item-type>
|
||||
<label>Breath Guidance Type</label>
|
||||
<description>Select a breath guidance type.</description>
|
||||
<tags>
|
||||
<tag>Control</tag>
|
||||
</tags>
|
||||
<state>
|
||||
<options>
|
||||
<option value="0">Light</option>
|
||||
<option value="1">Sound</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="humidity">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Humidity</label>
|
||||
<description>The current humidity in %.</description>
|
||||
<category>Humidity</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Humidity</tag>
|
||||
</tags>
|
||||
<state pattern="%.1f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="illuminance">
|
||||
<item-type>Number:Illuminance</item-type>
|
||||
<label>Illuminance</label>
|
||||
<description>The current illuminance in lux.</description>
|
||||
<category>Sun</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Light</tag>
|
||||
</tags>
|
||||
<state pattern="%.1f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="light">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Night Light</label>
|
||||
<description>The light switch channel allows to turn the night light on or off.</description>
|
||||
<category>Light</category>
|
||||
<tags>
|
||||
<tag>Control</tag>
|
||||
<tag>Light</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="noise">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Noise</label>
|
||||
<description>The current noise in dB.</description>
|
||||
<category>Noise</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Noise</tag>
|
||||
</tags>
|
||||
<state pattern="%d %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="preset">
|
||||
<item-type>String</item-type>
|
||||
<label>FM Preset</label>
|
||||
<description>The Device has 5 presets to store radio frequencies.</description>
|
||||
<tags>
|
||||
<tag>Control</tag>
|
||||
</tags>
|
||||
<state/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="relaxDuration">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Duration</label>
|
||||
<description>The duration of relax breathe program in minutes.</description>
|
||||
<category>Time</category>
|
||||
<tags>
|
||||
<tag>Control</tag>
|
||||
<tag>Duration</tag>
|
||||
</tags>
|
||||
<state>
|
||||
<options>
|
||||
<option value="5">5 Minutes</option>
|
||||
<option value="10">10 Minutes</option>
|
||||
<option value="15">15 Minutes</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="remainingTime">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Remaining Time</label>
|
||||
<description>Remaining time from an activated program.</description>
|
||||
<category>Time</category>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>Duration</tag>
|
||||
</tags>
|
||||
<state pattern="%1$tM:%1$tS min" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sunsetDuration">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Duration</label>
|
||||
<description>The duration of sunset program in minutes.</description>
|
||||
<category>Time</category>
|
||||
<tags>
|
||||
<tag>Control</tag>
|
||||
<tag>Duration</tag>
|
||||
</tags>
|
||||
<state>
|
||||
<options>
|
||||
<option value="5">5 Minutes</option>
|
||||
<option value="10">10 Minutes</option>
|
||||
<option value="15">15 Minutes</option>
|
||||
<option value="20">20 Minutes</option>
|
||||
<option value="30">30 Minutes</option>
|
||||
<option value="45">45 Minutes</option>
|
||||
<option value="60">60 Minutes</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>The current temperature in °C.</description>
|
||||
<category>Temperature</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
<state pattern="%.1f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -336,6 +336,7 @@
|
||||
<module>org.openhab.binding.solarwatt</module>
|
||||
<module>org.openhab.binding.somfymylink</module>
|
||||
<module>org.openhab.binding.somfytahoma</module>
|
||||
<module>org.openhab.binding.somneo</module>
|
||||
<module>org.openhab.binding.sonnen</module>
|
||||
<module>org.openhab.binding.sonos</module>
|
||||
<module>org.openhab.binding.sonyaudio</module>
|
||||
|
Loading…
Reference in New Issue
Block a user