[somneo] Add alarm support and other improvements (#14882)

* [somneo] Add alarm clock channels

Signed-off-by: Michael Myrcik <michael.myrcik@web.de>
pull/15109/head
0x4d4d 2023-06-17 12:55:27 +02:00 committed by GitHub
parent ff9254df75
commit b505f7b12c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1734 additions and 152 deletions

View File

@ -13,47 +13,62 @@ This binding does only support one Thing:
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 |
| Parameter | Values | Default |
| ---------------------------- | ----------------------------------------------------------- | ------- |
| hostname | Hostname or IP address of the device | - |
| port | Port number | 443 |
| refreshInterval | Interval the device is polled in sec | 30 |
| refreshIntervalAlarmExtended | Interval the device is polled in sec (alarm clock settings) | 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 |
| 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 |
| _Alarm_ | | | |
| alarm#snooze | Number:Time | RW | The duration of the snooze function in minutes |
| _Alarm[1...16]_ | | | |
| alarm[]#configured | Switch | RW | The duration of the snooze function in minutes |
| alarm[]#switch | Switch | RW | Turn the alarm clock on or off |
| alarm[]#repeatDay | Number | RW | The days on which the alarm is repeated |
| alarm[]#alarmTime | DateTime | RW | Alarm clock time |
| alarm[]#powerWake | Switch | RW | Turn the power wake on or off |
| alarm[]#powerWakeDelay | Number:Time | RW | How long after the normal alarm should Power Wake start |
| alarm[]#sunriseDuration | Number:Time | RW | The duration of sunrise program in minutes |
| alarm[]#sunriseBrightness | Dimmer | RW | The channel allows to set the sunrise light intensity |
| alarm[]#sunriseSchema | Number | RW | Choose a personal sunrise |
| alarm[]#sound | String | RW | The type of sound used for the alarm sound |
| alarm[]#volume | Dimmer | RW | Change the sound volume of the alarm clock |
## Full Example
@ -96,6 +111,59 @@ Switch PhilipsSomneo_AudioAux "AUX-Input" ["Switch", "P
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" }
// Alarm
Number:Time PhilipsSomneo_AlarmSnooze "Alarm Snooze" ["Control", "Duration"] { channel="somneo:hf367x:1:alarm#snooze" }
Switch PhilipsSomneo_Alarm1Switch "Alarm Clock" ["Switch", "Power"] { channel="somneo:hf367x:1:alarm1#switch" }
DateTime PhilipsSomneo_Alarm1Time "Alarm Clock Time" <Time> ["Control"] { channel="somneo:hf367x:1:alarm1#alarmTime" }
Number PhilipsSomneo_Alarm1RepeatDay "Repeat on [JS(somneorepeatday.js):%s]" <calendar> ["Control"] { channel="somneo:hf367x:1:alarm1#repeatDay" }
Switch PhilipsSomneo_Alarm1PowerWake "Power Wake" ["Control"] { channel="somneo:hf367x:1:alarm1#powerWake" }
Number:Time PhilipsSomneo_Alarm1PowerWakeDelay "Power Wake Delay" <Time> ["Control", "Duration"] { channel="somneo:hf367x:1:alarm1#powerWakeDelay" }
Number:Time PhilipsSomneo_Alarm1SunriseDuration "Sunrise Duration" <Time> ["Control", "Duration"] { channel="somneo:hf367x:1:alarm1#sunriseDuration" }
Dimmer PhilipsSomneo_Alarm1SunriseBrightness "Sunrise Brightness" <Light> ["Control", "Light"] { channel="somneo:hf367x:1:alarm1#sunriseBrightness" }
Number PhilipsSomneo_Alarm1SunriseSchema "Sunrise Color" <sunrise> ["Control", "ColorTemperature"] { channel="somneo:hf367x:1:alarm1#sunriseSchema" }
String PhilipsSomneo_Alarm1Sound "Sound" ["Control"] { channel="somneo:hf367x:1:alarm1#sound" }
Dimmer PhilipsSomneo_Alarm1Volume "Volume" <SoundVolume> ["Control", "SoundVolume"] { channel="somneo:hf367x:1:alarm1#volume" }
```
transform/somneorepeatday.js
```javascript
(function(i) {
if (i == 254) {
return "Daily";
}
if (i == 192) {
return "Weekend";
}
if (i == 62) {
return "Weekdays";
}
days = [];
[
["Sunday", 128],
["Saturday", 64],
["Friday", 32],
["Thursday", 16],
["Wednesday", 8],
["Tuesday", 4],
["Monday", 2]
].forEach(function (x) {
if (i >= x[1]) {
days.push(x[0]);
i = i % x[1];
}
});
if (days.length === 0) {
return "Never";
}
if (days.length == 1) {
return days[0];
}
return days.reverse().map(function(x) {
return x.slice(0, 2);
}).join(" ");
})(input)
```
somneo.sitemap:
@ -140,6 +208,81 @@ sitemap somneo label="Philips Somneo" {
Default item=PhilipsSomneo_AudioPreset visibility=[PhilipsSomneo_AudioRadio==PLAY]
Default item=PhilipsSomneo_AudioFrequency visibility=[PhilipsSomneo_AudioRadio==PLAY]
}
Frame label="Alarm Common" {
Setpoint item=PhilipsSomneo_AlarmSnooze minValue=1 maxValue=20
Text label="Alarms" icon="settings" {
Default item=PhilipsSomneo_Alarm3Configured
Default item=PhilipsSomneo_Alarm4Configured
Default item=PhilipsSomneo_Alarm5Configured
Default item=PhilipsSomneo_Alarm6Configured
Default item=PhilipsSomneo_Alarm7Configured
Default item=PhilipsSomneo_Alarm8Configured
Default item=PhilipsSomneo_Alarm9Configured
Default item=PhilipsSomneo_Alarm10Configured
Default item=PhilipsSomneo_Alarm11Configured
Default item=PhilipsSomneo_Alarm12Configured
Default item=PhilipsSomneo_Alarm13Configured
Default item=PhilipsSomneo_Alarm14Configured
Default item=PhilipsSomneo_Alarm15Configured
Default item=PhilipsSomneo_Alarm16Configured
}
}
Frame label="Alarm [1]" {
Default item=PhilipsSomneo_Alarm1Switch
Default item=PhilipsSomneo_Alarm1Time
Default item=PhilipsSomneo_Alarm1PowerWake
Slider item=PhilipsSomneo_Alarm1PowerWakeDelay minValue=0 maxValue=59
Slider item=PhilipsSomneo_Alarm1RepeatDay minValue=0 maxValue=254 step=2
Text label="Settings" icon="settings" {
Default item=PhilipsSomneo_Alarm1SunriseDuration
Slider item=PhilipsSomneo_Alarm1SunriseBrightness
Selection item=PhilipsSomneo_Alarm1SunriseSchema
Default item=PhilipsSomneo_Alarm1Sound
Default item=PhilipsSomneo_Alarm1Volume
}
}
Frame label="Alarm [2]"{
Default item=PhilipsSomneo_Alarm2Switch
Default item=PhilipsSomneo_Alarm2Time
Default item=PhilipsSomneo_Alarm2PowerWake
Slider item=PhilipsSomneo_Alarm2PowerWakeDelay minValue=0 maxValue=59
Slider item=PhilipsSomneo_Alarm2RepeatDay minValue=0 maxValue=254 step=2
Text label="Settings" icon="settings" {
Default item=PhilipsSomneo_Alarm2SunriseDuration
Slider item=PhilipsSomneo_Alarm2SunriseBrightness minValue=4 maxValue=100 step=4
Selection item=PhilipsSomneo_Alarm2SunriseSchema
Default item=PhilipsSomneo_Alarm2Sound
Slider item=PhilipsSomneo_Alarm2Volume minValue=4 maxValue=100 step=4
}
}
Frame label="Alarm [3]" visibility=[PhilipsSomneo_Alarm3Configured==ON] {
Default item=PhilipsSomneo_Alarm3Switch
Default item=PhilipsSomneo_Alarm3Time
Default item=PhilipsSomneo_Alarm3PowerWake
Slider item=PhilipsSomneo_Alarm3PowerWakeDelay minValue=0 maxValue=59
Slider item=PhilipsSomneo_Alarm3RepeatDay minValue=0 maxValue=254 step=2
Text label="Settings" icon="settings" {
Default item=PhilipsSomneo_Alarm3SunriseDuration
Slider item=PhilipsSomneo_Alarm3SunriseBrightness minValue=4 maxValue=100 step=4
Selection item=PhilipsSomneo_Alarm3SunriseSchema
Default item=PhilipsSomneo_Alarm3Sound
Slider item=PhilipsSomneo_Alarm3Volume minValue=4 maxValue=100 step=4
}
}
Frame label="Alarm [4]" visibility=[PhilipsSomneo_Alarm4Configured==ON] {
Default item=PhilipsSomneo_Alarm4Switch
Default item=PhilipsSomneo_Alarm4Time
Default item=PhilipsSomneo_Alarm4PowerWake
Slider item=PhilipsSomneo_Alarm4PowerWakeDelay minValue=0 maxValue=59
Slider item=PhilipsSomneo_Alarm4RepeatDay minValue=0 maxValue=254 step=2
Text label="Settings" icon="settings" {
Default item=PhilipsSomneo_Alarm4SunriseDuration
Slider item=PhilipsSomneo_Alarm4SunriseBrightness minValue=4 maxValue=100 step=4
Selection item=PhilipsSomneo_Alarm4SunriseSchema
Default item=PhilipsSomneo_Alarm4Sound
Slider item=PhilipsSomneo_Alarm4Volume minValue=4 maxValue=100 step=4
}
}
}
```

View File

@ -58,6 +58,21 @@ public class SomneoBindingConstants {
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";
public static final String CHANNEL_ALARM_SNOOZE = "alarm#snooze";
public static final String CHANNEL_ALARM_CONFIGURED = "alarm%d#configured";
public static final String CHANNEL_ALARM_SWITCH = "alarm%d#switch";
public static final String CHANNEL_ALARM_TIME = "alarm%d#alarmTime";
public static final String CHANNEL_ALARM_REPEAT_DAY = "alarm%d#repeatDay";
public static final String CHANNEL_ALARM_POWER_WAKE = "alarm%d#powerWake";
public static final String CHANNEL_ALARM_POWER_WAKE_DELAY = "alarm%d#powerWakeDelay";
public static final String CHANNEL_ALARM_SUNRISE_DURATION = "alarm%d#sunriseDuration";
public static final String CHANNEL_ALARM_SUNRISE_BRIGHTNESS = "alarm%d#sunriseBrightness";
public static final String CHANNEL_ALARM_SUNRISE_SCHEMA = "alarm%d#sunriseSchema";
public static final String CHANNEL_ALARM_SOUND = "alarm%d#sound";
public static final String CHANNEL_ALARM_VOLUME = "alarm%d#volume";
// Regex for alarm channels
public static final String CHANNEL_ALARM_PREFIX_REGEX = "^alarm(\\d{1,2})#\\w+$";
// List of all Web Service Endpoints
public static final String AUDIO_ENDPOINT = "/1/wuply";
@ -71,4 +86,8 @@ public class SomneoBindingConstants {
public static final String SENSORS_ENDPOINT = "/1/wusrd";
public static final String SUNSET_ENDPOINT = "/1/wudsk";
public static final String WIFI_ENDPOINT = "/0/wifi";
public static final String ALARM_STATES_ENDPOINT = "/1/wualm/aenvs";
public static final String ALARM_SCHEDULES_ENDPOINT = "/1/wualm/aalms";
public static final String ALARM_SETTINGS_ENDPOINT = "/1/wualm";
public static final String ALARM_EDIT_ENDPOINT = "/1/wualm/prfwu";
}

View File

@ -25,5 +25,6 @@ public class SomneoConfiguration {
public String hostname = "";
public int port = 443;
public int refreshInterval = 30;
public int refreshIntervalAlarmExtended = 300;
public boolean ignoreSSLErrors = false;
}

View File

@ -16,14 +16,20 @@ import static org.openhab.binding.somneo.internal.SomneoBindingConstants.*;
import java.io.EOFException;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.AlarmSchedulesData;
import org.openhab.binding.somneo.internal.model.AlarmSettingsData;
import org.openhab.binding.somneo.internal.model.AlarmStateData;
import org.openhab.binding.somneo.internal.model.AudioData;
import org.openhab.binding.somneo.internal.model.DeviceData;
import org.openhab.binding.somneo.internal.model.FirmwareData;
@ -35,6 +41,7 @@ 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.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.NextPreviousType;
import org.openhab.core.library.types.OnOffType;
@ -52,6 +59,7 @@ 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.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -70,10 +78,13 @@ public class SomneoHandler extends BaseThingHandler {
private final SomneoPresetStateDescriptionProvider provider;
private final Pattern alarmPattern;
/**
* Job to poll data from the device.
*/
private @Nullable ScheduledFuture<?> pollingJob;
private @Nullable ScheduledFuture<?> pollingJobExtended;
/**
* Job to count down the remaining program time.
@ -97,6 +108,7 @@ public class SomneoHandler extends BaseThingHandler {
super(thing);
this.httpClientProvider = httpClientProvider;
this.provider = provider;
this.alarmPattern = Objects.requireNonNull(Pattern.compile(CHANNEL_ALARM_PREFIX_REGEX));
}
@Override
@ -104,20 +116,51 @@ public class SomneoHandler extends BaseThingHandler {
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 {
final SomneoHttpConnector connector = this.connector;
if (connector == null) {
return;
}
final Matcher matcher = alarmPattern.matcher(channelId);
int alarmPosition = 0;
if (matcher.matches()) {
// Replace alarm channel index with string format placeholder to match
// constants.
alarmPosition = Integer.parseInt(matcher.group(1));
channelId = channelId.replace(alarmPosition + "#", "%d#");
}
if (command instanceof RefreshType) {
if (channelId.equals(CHANNEL_ALARM_SNOOZE)) {
final State snooze = connector.fetchSnoozeDuration();
updateState(CHANNEL_ALARM_SNOOZE, snooze);
} else if (channelId.startsWith("alarm")) {
updateAlarmExtended(alarmPosition);
} else if (channelId.startsWith("sensor")) {
updateSensors();
} else if (channelId.startsWith("light")) {
updateLights();
} else if (channelId.equals(CHANNEL_RELAX_REMAINING_TIME)
|| channelId.equals(CHANNEL_SUNSET_REMAINING_TIME)) {
updateRemainingTimer();
} else if (channelId.equals(CHANNEL_AUDIO_FREQUENCY)) {
updateFrequency();
} else if (channelId.startsWith("audio")) {
updateLights();
} else if (channelId.startsWith("sunset")) {
updateSunset();
} else if (channelId.startsWith("relax")) {
updateRelax();
} else {
this.poll();
}
return;
}
switch (channelId) {
case CHANNEL_AUDIO_AUX:
if (command instanceof OnOffType) {
if (command instanceof OnOffType onOff) {
boolean isOn = OnOffType.ON.equals(command);
connector.switchAux(isOn);
@ -141,7 +184,7 @@ public class SomneoHandler extends BaseThingHandler {
}
break;
case CHANNEL_AUDIO_RADIO:
if (command instanceof PlayPauseType) {
if (command instanceof PlayPauseType playPause) {
boolean isPlaying = PlayPauseType.PLAY.equals(command);
connector.switchRadio(isPlaying);
@ -161,8 +204,8 @@ public class SomneoHandler extends BaseThingHandler {
}
break;
case CHANNEL_AUDIO_VOLUME:
if (command instanceof PercentType) {
connector.setAudioVolume(Integer.parseInt(command.toFullString()));
if (command instanceof PercentType percent) {
connector.setAudioVolume(percent.intValue());
}
break;
case CHANNEL_LIGHT_MAIN:
@ -177,8 +220,8 @@ public class SomneoHandler extends BaseThingHandler {
updateState(CHANNEL_SUNSET_SWITCH, OnOffType.OFF);
}
}
if (command instanceof PercentType) {
int level = Integer.parseInt(command.toFullString());
if (command instanceof PercentType percent) {
int level = percent.intValue();
if (level > 0) {
connector.setMainLightDimmer(level);
@ -205,23 +248,23 @@ public class SomneoHandler extends BaseThingHandler {
}
break;
case CHANNEL_RELAX_BREATHING_RATE:
if (command instanceof DecimalType) {
connector.setRelaxBreathingRate(Integer.parseInt(command.toFullString()));
if (command instanceof DecimalType decimal) {
connector.setRelaxBreathingRate(decimal.intValue());
}
break;
case CHANNEL_RELAX_DURATION:
if (command instanceof DecimalType) {
connector.setRelaxDuration(Integer.parseInt(command.toFullString()));
if (command instanceof QuantityType quantity) {
connector.setRelaxDuration(quantity.intValue());
}
break;
case CHANNEL_RELAX_GUIDANCE_TYPE:
if (command instanceof DecimalType) {
connector.setRelaxGuidanceType(Integer.parseInt(command.toFullString()));
if (command instanceof DecimalType decimal) {
connector.setRelaxGuidanceType(decimal.intValue());
}
break;
case CHANNEL_RELAX_LIGHT_INTENSITY:
if (command instanceof PercentType) {
connector.setRelaxLightIntensity(Integer.parseInt(command.toFullString()));
if (command instanceof PercentType percent) {
connector.setRelaxLightIntensity(percent.intValue());
}
break;
case CHANNEL_RELAX_SWITCH:
@ -241,8 +284,8 @@ public class SomneoHandler extends BaseThingHandler {
}
break;
case CHANNEL_RELAX_VOLUME:
if (command instanceof PercentType) {
connector.setRelaxVolume(Integer.parseInt(command.toFullString()));
if (command instanceof PercentType percent) {
connector.setRelaxVolume(percent.intValue());
}
break;
case CHANNEL_SUNSET_AMBIENT_NOISE:
@ -251,18 +294,18 @@ public class SomneoHandler extends BaseThingHandler {
}
break;
case CHANNEL_SUNSET_COLOR_SCHEMA:
if (command instanceof DecimalType) {
connector.setSunsetColorSchema(Integer.parseInt(command.toFullString()));
if (command instanceof DecimalType decimal) {
connector.setSunsetColorSchema(decimal.intValue());
}
break;
case CHANNEL_SUNSET_DURATION:
if (command instanceof DecimalType) {
connector.setSunsetDuration(Integer.parseInt(command.toFullString()));
if (command instanceof QuantityType quantity) {
connector.setSunsetDuration(quantity.intValue());
}
break;
case CHANNEL_SUNSET_LIGHT_INTENSITY:
if (command instanceof PercentType) {
connector.setSunsetLightIntensity(Integer.parseInt(command.toFullString()));
if (command instanceof PercentType percent) {
connector.setSunsetLightIntensity(percent.intValue());
}
break;
case CHANNEL_SUNSET_SWITCH:
@ -282,8 +325,84 @@ public class SomneoHandler extends BaseThingHandler {
}
break;
case CHANNEL_SUNSET_VOLUME:
if (command instanceof PercentType) {
connector.setSunsetVolume(Integer.parseInt(command.toFullString()));
if (command instanceof PercentType percent) {
connector.setSunsetVolume(percent.intValue());
}
break;
case CHANNEL_ALARM_SNOOZE:
if (command instanceof QuantityType quantity) {
connector.setAlarmSnooze(quantity.intValue());
}
break;
case CHANNEL_ALARM_CONFIGURED:
if (alarmPosition > 2) {
if (command instanceof OnOffType onOff) {
connector.toggleAlarmConfiguration(alarmPosition, onOff);
if (OnOffType.ON.equals(command)) {
updateAlarmExtended(alarmPosition);
} else {
resetAlarm(alarmPosition);
}
}
} else {
logger.info("Alarm 1 and 2 can not be unset");
}
break;
case CHANNEL_ALARM_SWITCH:
if (command instanceof OnOffType onOff) {
connector.toggleAlarm(alarmPosition, onOff);
updateAlarmExtended(alarmPosition);
}
break;
case CHANNEL_ALARM_TIME:
if (command instanceof DateTimeType decimal) {
connector.setAlarmTime(alarmPosition, decimal);
}
break;
case CHANNEL_ALARM_REPEAT_DAY:
if (command instanceof DecimalType decimal) {
connector.setAlarmRepeatDay(alarmPosition, decimal);
}
break;
case CHANNEL_ALARM_POWER_WAKE:
if (command instanceof OnOffType onOff) {
connector.toggleAlarmPowerWake(alarmPosition, onOff);
if (OnOffType.OFF.equals(command)) {
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_POWER_WAKE_DELAY, alarmPosition),
QuantityType.valueOf(0, Units.MINUTE));
}
}
break;
case CHANNEL_ALARM_POWER_WAKE_DELAY:
if (command instanceof QuantityType quantity) {
connector.setAlarmPowerWakeDelay(alarmPosition, quantity);
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_POWER_WAKE, alarmPosition), OnOffType.ON);
}
break;
case CHANNEL_ALARM_SUNRISE_DURATION:
if (command instanceof QuantityType quantity) {
connector.setAlarmSunriseDuration(alarmPosition, quantity);
}
break;
case CHANNEL_ALARM_SUNRISE_BRIGHTNESS:
if (command instanceof PercentType percent) {
connector.setAlarmSunriseBrightness(alarmPosition, percent);
}
break;
case CHANNEL_ALARM_SUNRISE_SCHEMA:
if (command instanceof DecimalType decimal) {
connector.setAlarmSunriseSchema(alarmPosition, decimal);
}
break;
case CHANNEL_ALARM_SOUND:
if (command instanceof StringType) {
connector.setAlarmSound(alarmPosition, (StringType) command);
}
break;
case CHANNEL_ALARM_VOLUME:
if (command instanceof PercentType percent) {
connector.setAlarmVolume(alarmPosition, percent);
}
break;
default:
@ -391,19 +510,30 @@ public class SomneoHandler extends BaseThingHandler {
return;
}
int refreshInterval = getConfigAs(SomneoConfiguration.class).refreshInterval;
logger.debug("Start polling job at interval {}s", refreshInterval);
final SomneoConfiguration config = getConfigAs(SomneoConfiguration.class);
final int refreshInterval = config.refreshInterval;
logger.debug("Start default polling job at interval {}s", refreshInterval);
this.pollingJob = scheduler.scheduleWithFixedDelay(this::poll, 0, refreshInterval, TimeUnit.SECONDS);
final int refreshIntervalAlarmExtended = config.refreshIntervalAlarmExtended;
logger.debug("Start extended alarm polling job at interval {}s", refreshIntervalAlarmExtended);
this.pollingJobExtended = scheduler.scheduleWithFixedDelay(this::pollAlarmExtended, 0,
refreshIntervalAlarmExtended, TimeUnit.SECONDS);
}
private void stopPolling() {
final ScheduledFuture<?> pollingJob = this.pollingJob;
if (pollingJob == null || pollingJob.isCancelled()) {
return;
if (pollingJob != null) {
pollingJob.cancel(true);
this.pollingJob = null;
}
final ScheduledFuture<?> pollingJobExtended = this.pollingJobExtended;
if (pollingJobExtended != null) {
pollingJobExtended.cancel(true);
this.pollingJobExtended = null;
}
pollingJob.cancel(true);
this.pollingJob = null;
logger.debug("HTTP polling stopped.");
}
@ -414,41 +544,20 @@ public class SomneoHandler extends BaseThingHandler {
}
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());
updateSensors();
final LightData lightData = connector.fetchLightData();
updateState(CHANNEL_LIGHT_MAIN, lightData.getMainLightState());
updateState(CHANNEL_LIGHT_NIGHT, lightData.getNightLightState());
lastLightBrightness = lightData.getMainLightLevel();
updateLights();
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());
updateSunset();
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());
updateRelax();
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());
updateAudio();
updateFrequency();
updateAlarm();
updateRemainingTimer();
updateStatus(ThingStatus.ONLINE);
@ -465,6 +574,70 @@ public class SomneoHandler extends BaseThingHandler {
}
}
private void updateAudio() throws TimeoutException, InterruptedException, ExecutionException {
final SomneoHttpConnector connector = this.connector;
if (connector == null) {
return;
}
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());
}
private void updateRelax() throws TimeoutException, InterruptedException, ExecutionException {
final SomneoHttpConnector connector = this.connector;
if (connector == null) {
return;
}
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());
}
private void updateSunset() throws TimeoutException, InterruptedException, ExecutionException {
final SomneoHttpConnector connector = this.connector;
if (connector == null) {
return;
}
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());
}
private void updateLights() throws TimeoutException, InterruptedException, ExecutionException {
final SomneoHttpConnector connector = this.connector;
if (connector == null) {
return;
}
final LightData lightData = connector.fetchLightData();
updateState(CHANNEL_LIGHT_MAIN, lightData.getMainLightState());
updateState(CHANNEL_LIGHT_NIGHT, lightData.getNightLightState());
lastLightBrightness = lightData.getMainLightLevel();
}
private void updateSensors() throws TimeoutException, InterruptedException, ExecutionException {
final SomneoHttpConnector connector = this.connector;
if (connector == null) {
return;
}
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());
}
private void updateFrequency() throws TimeoutException, InterruptedException, ExecutionException {
final SomneoHttpConnector connector = this.connector;
if (connector == null) {
@ -541,4 +714,117 @@ public class SomneoHandler extends BaseThingHandler {
stopRemainingTimer();
}
}
private void updateAlarm() throws TimeoutException, InterruptedException, ExecutionException {
final SomneoHttpConnector connector = this.connector;
if (connector == null) {
return;
}
final State snooze = connector.fetchSnoozeDuration();
updateState(CHANNEL_ALARM_SNOOZE, snooze);
final AlarmStateData alarmState = connector.fetchAlarmStateData();
final AlarmSchedulesData alarmSchedulesData = connector.fetchAlarmScheduleData();
for (int i = 1; i <= alarmState.getAlarmCount(); i++) {
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_CONFIGURED, i), alarmState.getConfiguredState(i));
if (OnOffType.ON.equals(alarmState.getConfiguredState(i))) {
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_SWITCH, i), alarmState.getEnabledState(i));
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_TIME, i),
alarmSchedulesData.getAlarmTimeState(i));
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_REPEAT_DAY, i),
alarmSchedulesData.getRepeatDayState(i));
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_POWER_WAKE, i), alarmState.getPowerWakeState(i));
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_POWER_WAKE_DELAY, i),
alarmState.getPowerWakeDelayState(i, alarmSchedulesData.getAlarmTime(i)));
} else {
resetAlarm(i);
}
}
}
private void pollAlarmExtended() {
final SomneoHttpConnector connector = this.connector;
if (connector == null) {
return;
}
try {
final AlarmStateData alarmState = connector.fetchAlarmStateData();
for (int i = 1; i <= alarmState.getAlarmCount(); i++) {
if (OnOffType.ON.equals(alarmState.getConfiguredState(i))) {
updateAlarmExtended(i);
} else {
resetAlarm(i);
}
}
} catch (InterruptedException e) {
logger.debug("Polling extended alarm 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 updateAlarmExtended(int position) throws TimeoutException, InterruptedException, ExecutionException {
final SomneoHttpConnector connector = this.connector;
if (connector == null) {
return;
}
final AlarmSettingsData alarmSettings = connector.fetchAlarmSettingsData(position);
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_CONFIGURED, position),
alarmSettings.getConfiguredState());
if (OnOffType.ON.equals(alarmSettings.getConfiguredState())) {
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_SWITCH, position), alarmSettings.getEnabledState());
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_POWER_WAKE, position),
alarmSettings.getPowerWakeState());
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_POWER_WAKE_DELAY, position),
alarmSettings.getPowerWakeDelayState());
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_TIME, position), alarmSettings.getAlarmTimeState());
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_REPEAT_DAY, position),
alarmSettings.getRepeatDayState());
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_SUNRISE_DURATION, position),
alarmSettings.getSunriseDurationInMin());
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_SUNRISE_BRIGHTNESS, position),
alarmSettings.getSunriseBrightness());
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_SUNRISE_SCHEMA, position),
alarmSettings.getSunriseSchema());
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_SOUND, position), alarmSettings.getSound());
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_VOLUME, position), alarmSettings.getSoundVolume());
} else {
resetAlarm(position);
}
}
private void resetAlarm(int position) throws TimeoutException, InterruptedException, ExecutionException {
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_SWITCH, position), UnDefType.UNDEF);
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_POWER_WAKE, position), UnDefType.UNDEF);
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_POWER_WAKE_DELAY, position), UnDefType.UNDEF);
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_TIME, position), UnDefType.UNDEF);
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_REPEAT_DAY, position), UnDefType.UNDEF);
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_SUNRISE_DURATION, position), UnDefType.UNDEF);
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_SUNRISE_BRIGHTNESS, position), UnDefType.UNDEF);
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_SUNRISE_SCHEMA, position), UnDefType.UNDEF);
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_SOUND, position), UnDefType.UNDEF);
updateState(formatAlarmChannelIdByIndex(CHANNEL_ALARM_VOLUME, position), UnDefType.UNDEF);
}
private String formatAlarmChannelIdByIndex(String channelId, int index) {
final String channelIdFormated = String.format(channelId, index);
if (channelIdFormated == null) {
return "";
}
return channelIdFormated;
}
}

View File

@ -16,10 +16,13 @@ import static org.openhab.binding.somneo.internal.SomneoBindingConstants.*;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.time.LocalTime;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.measure.quantity.Time;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
@ -28,6 +31,9 @@ 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.AlarmSchedulesData;
import org.openhab.binding.somneo.internal.model.AlarmSettingsData;
import org.openhab.binding.somneo.internal.model.AlarmStateData;
import org.openhab.binding.somneo.internal.model.AudioData;
import org.openhab.binding.somneo.internal.model.DeviceData;
import org.openhab.binding.somneo.internal.model.FirmwareData;
@ -40,10 +46,21 @@ 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.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
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.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* The {@link SomneoHttpConnector} is responsible for sending commands.
@ -290,11 +307,163 @@ public class SomneoHttpConnector {
return executeUrl("GET", PRESET_ENDPOINT, PresetData.class);
}
public AlarmStateData fetchAlarmStateData() throws TimeoutException, InterruptedException, ExecutionException {
return executeUrl("GET", ALARM_STATES_ENDPOINT, AlarmStateData.class);
}
public AlarmSchedulesData fetchAlarmScheduleData()
throws TimeoutException, InterruptedException, ExecutionException {
return executeUrl("GET", ALARM_SCHEDULES_ENDPOINT, AlarmSchedulesData.class);
}
public AlarmSettingsData fetchAlarmSettingsData(final int position)
throws TimeoutException, InterruptedException, ExecutionException {
final String responseBody = executeUrl("PUT", ALARM_SETTINGS_ENDPOINT, "{\"prfnr\":" + (position) + "}");
AlarmSettingsData data = (AlarmSettingsData) gson.fromJson(responseBody, AlarmSettingsData.class);
if (data == null) {
return new AlarmSettingsData();
}
return data;
}
public State fetchSnoozeDuration() throws TimeoutException, InterruptedException, ExecutionException {
final JsonObject response = executeUrl("GET", ALARM_SETTINGS_ENDPOINT, JsonObject.class);
final JsonElement snooze = response.get("snztm");
if (snooze == null) {
return UnDefType.NULL;
}
return new QuantityType<>(snooze.getAsInt(), Units.MINUTE);
}
public void setAlarmSnooze(int snooze) throws TimeoutException, InterruptedException, ExecutionException {
final JsonObject data = new JsonObject();
data.addProperty("snztm", snooze);
executeUrl("PUT", ALARM_SETTINGS_ENDPOINT, data);
}
public void toggleAlarmConfiguration(int position, OnOffType command)
throws TimeoutException, InterruptedException, ExecutionException {
final AlarmSettingsData data = AlarmSettingsData.withDefaultValues(position);
if (OnOffType.ON.equals(command)) {
final LocalTime now = LocalTime.now();
if (now == null) {
return;
}
data.setConfigured(true);
data.setEnabled(true);
data.setAlarmTime(now);
data.setRepeatDay(0);
} else {
data.setConfigured(false);
data.setEnabled(false);
}
executeUrl("PUT", ALARM_EDIT_ENDPOINT, data);
}
public void toggleAlarm(int position, OnOffType enabled)
throws TimeoutException, InterruptedException, ExecutionException {
final AlarmSettingsData data = new AlarmSettingsData();
data.setPosition(position);
data.setEnabledState(enabled);
if (OnOffType.ON.equals(enabled)) {
data.setConfigured(true);
}
executeUrl("PUT", ALARM_EDIT_ENDPOINT, data);
}
public void setAlarmTime(int position, DateTimeType time)
throws TimeoutException, InterruptedException, ExecutionException {
final AlarmSettingsData data = fetchAlarmSettingsData(position);
data.setConfigured(true);
data.setAlarmTime(time);
executeUrl("PUT", ALARM_EDIT_ENDPOINT, data);
}
public void setAlarmRepeatDay(int position, DecimalType days)
throws TimeoutException, InterruptedException, ExecutionException {
final AlarmSettingsData data = new AlarmSettingsData();
data.setPosition(position);
data.setConfigured(true);
data.setRepeatDayState(days);
executeUrl("PUT", ALARM_EDIT_ENDPOINT, data);
}
public void toggleAlarmPowerWake(int position, OnOffType state)
throws TimeoutException, InterruptedException, ExecutionException {
final AlarmSettingsData data = fetchAlarmSettingsData(position);
data.setPosition(position);
data.setPowerWakeState(state);
executeUrl("PUT", ALARM_EDIT_ENDPOINT, data);
}
public void setAlarmPowerWakeDelay(int position, QuantityType<Time> time)
throws TimeoutException, InterruptedException, ExecutionException {
final AlarmSettingsData data = fetchAlarmSettingsData(position);
data.setConfigured(true);
data.setPowerWakeDelayState(time);
executeUrl("PUT", ALARM_EDIT_ENDPOINT, data);
}
public void setAlarmSunriseDuration(int position, QuantityType<Time> duration)
throws TimeoutException, InterruptedException, ExecutionException {
final AlarmSettingsData data = new AlarmSettingsData();
data.setPosition(position);
data.setConfigured(true);
data.setSunriseDurationState(duration);
executeUrl("PUT", ALARM_EDIT_ENDPOINT, data);
}
public void setAlarmSunriseBrightness(int position, PercentType percent)
throws TimeoutException, InterruptedException, ExecutionException {
final AlarmSettingsData data = new AlarmSettingsData();
data.setPosition(position);
data.setConfigured(true);
data.setSunriseBrightnessState(percent);
executeUrl("PUT", ALARM_EDIT_ENDPOINT, data);
}
public void setAlarmSunriseSchema(int position, DecimalType schema)
throws TimeoutException, InterruptedException, ExecutionException {
final AlarmSettingsData data = new AlarmSettingsData();
data.setPosition(position);
data.setConfigured(true);
data.setSunriseSchemaState(schema);
executeUrl("PUT", ALARM_EDIT_ENDPOINT, data);
}
public void setAlarmSound(int position, StringType sound)
throws TimeoutException, InterruptedException, ExecutionException {
final AlarmSettingsData data = new AlarmSettingsData();
data.setPosition(position);
data.setConfigured(true);
data.setAlarmSoundState(sound);
executeUrl("PUT", ALARM_EDIT_ENDPOINT, data);
}
public void setAlarmVolume(int position, PercentType volume)
throws TimeoutException, InterruptedException, ExecutionException {
final AlarmSettingsData data = new AlarmSettingsData();
data.setPosition(position);
data.setConfigured(true);
data.setAlarmVolumeState(volume);
executeUrl("PUT", ALARM_EDIT_ENDPOINT, data);
}
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;
final String responseBody = executeUrl(httpMethod, endpoint, (String) null);
return gson.fromJson(responseBody, classOfT);
}
private void executeUrl(String httpMethod, String endpoint, Object data)

View File

@ -0,0 +1,89 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.somneo.internal.model;
import java.time.LocalTime;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
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
*/
public class AlarmSchedulesData {
/**
* None = 0,
* Monday = 2,
* Tuesday = 4,
* Wednesday = 8,
* Thursday = 16,
* Friday = 32,
* Saturday = 64,
* Sunday = 128
*/
@SerializedName("daynm")
private List<Integer> repeatDays;
@SerializedName("almhr")
private List<Integer> hours;
@SerializedName("almmn")
private List<Integer> minutes;
public @NonNull State getRepeatDayState(int position) {
final List<Integer> repeatDays = this.repeatDays;
if (repeatDays == null) {
return UnDefType.NULL;
}
final Integer repeatDay = repeatDays.get(position - 1);
if (repeatDay == null) {
return UnDefType.NULL;
}
return new DecimalType(repeatDay);
}
public LocalTime getAlarmTime(int position) {
final List<Integer> hours = this.hours;
if (hours == null) {
return null;
}
final List<Integer> minutes = this.minutes;
if (minutes == null) {
return null;
}
final Integer hour = hours.get(position - 1);
final Integer minute = minutes.get(position - 1);
return LocalTime.of(hour, minute);
}
public @NonNull State getAlarmTimeState(int position) {
final LocalTime time = getAlarmTime(position);
if (time == null) {
return UnDefType.NULL;
}
final String alarmTimeString = String.format("%02d:%02d:00", time.getHour(), time.getMinute());
if (alarmTimeString == null) {
return UnDefType.NULL;
}
return DateTimeType.valueOf(alarmTimeString);
}
}

View File

@ -0,0 +1,352 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.somneo.internal.model;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import javax.measure.quantity.Time;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
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.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 AlarmSettingsData {
private static final int POWER_WAKE_ENABLED = 255;
private static final int POWER_WAKE_DISABLED = 0;
@SerializedName("prfnr")
private @Nullable Integer position;
@SerializedName("prfvs")
private @Nullable Boolean configured;
@SerializedName("prfen")
private @Nullable Boolean enabled;
/**
* None = 0,
* Monday = 2,
* Tuesday = 4,
* Wednesday = 8,
* Thursday = 16,
* Friday = 32,
* Saturday = 64,
* Sunday = 128
*/
@SerializedName("daynm")
private @Nullable Integer repeatDay;
@SerializedName("almhr")
private @Nullable Integer hour;
@SerializedName("almmn")
private @Nullable Integer minute;
/**
* Brightness range from 0 to 25.
*/
@SerializedName("curve")
private @Nullable Integer sunriseBrightness;
@SerializedName("durat")
private @Nullable Integer sunriseDurationInMin;
@SerializedName("ctype")
private @Nullable Integer sunriseSchema;
@SerializedName("snddv")
private @Nullable String soundSource;
@SerializedName("sndch")
private @Nullable String soundChannel;
@SerializedName("sndlv")
private @Nullable Integer soundVolume;
@SerializedName("pwrsz")
private @Nullable Integer powerWake;
@SerializedName("pszhr")
private @Nullable Integer powerWakeHour;
@SerializedName("pszmn")
private @Nullable Integer powerWakeMinute;
public void setPosition(Integer position) {
this.position = position;
}
public State getConfiguredState() {
final Boolean configured = this.configured;
if (configured == null) {
return UnDefType.NULL;
}
return OnOffType.from(configured);
}
public void setConfigured(Boolean configured) {
this.configured = configured;
}
public State getEnabledState() {
final Boolean enabled = this.enabled;
if (enabled == null) {
return UnDefType.NULL;
}
return OnOffType.from(enabled);
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setEnabledState(OnOffType enabled) {
setEnabled(OnOffType.ON.equals(enabled));
}
public void setAlarmTime(LocalTime time) {
Integer powerWakeDelay = getPowerWakeDelay();
if (powerWakeDelay == null) {
powerWakeDelay = 0;
}
this.hour = time.getHour();
this.minute = time.getMinute();
final Integer powerWake = this.powerWake;
if (powerWake != null && powerWake.intValue() == POWER_WAKE_ENABLED) {
LocalTime powerWakeTime = time.plusMinutes(powerWakeDelay);
this.powerWakeHour = powerWakeTime.getHour();
this.powerWakeMinute = powerWakeTime.getMinute();
}
}
public void setAlarmTime(DateTimeType timeState) {
final ZonedDateTime zonedTime = timeState.getZonedDateTime();
final LocalTime time = LocalTime.of(zonedTime.getHour(), zonedTime.getMinute());
if (time == null) {
return;
}
setAlarmTime(time);
}
public State getRepeatDayState() {
final Integer repeatDay = this.repeatDay;
if (repeatDay == null) {
return UnDefType.NULL;
}
return new DecimalType(repeatDay);
}
public void setRepeatDay(int days) {
this.repeatDay = days;
}
public void setRepeatDayState(DecimalType days) {
setRepeatDay(days.intValue());
}
public State getAlarmTimeState() {
final Integer hour = this.hour;
if (hour == null) {
return UnDefType.NULL;
}
final Integer minute = this.minute;
if (minute == null) {
return UnDefType.NULL;
}
final String alarmTimeString = String.format("%02d:%02d:00", hour, minute);
if (alarmTimeString == null) {
return UnDefType.NULL;
}
return DateTimeType.valueOf(alarmTimeString);
}
public State getPowerWakeState() {
final Integer powerWake = this.powerWake;
if (powerWake == null) {
return UnDefType.NULL;
}
return OnOffType.from((int) powerWake == POWER_WAKE_ENABLED);
}
public void setPowerWakeState(OnOffType state) {
if (OnOffType.ON.equals(state)) {
powerWake = POWER_WAKE_ENABLED;
powerWakeHour = hour;
powerWakeMinute = minute;
} else {
powerWake = POWER_WAKE_DISABLED;
powerWakeHour = 0;
powerWakeMinute = 0;
}
}
public @Nullable Integer getPowerWakeDelay() {
if (OnOffType.OFF.equals(getPowerWakeState())) {
return Integer.valueOf(0);
}
final Integer hour = this.hour;
if (hour == null) {
return null;
}
final Integer minute = this.minute;
if (minute == null) {
return null;
}
final Integer powerWakeHour = this.powerWakeHour;
if (powerWakeHour == null) {
return null;
}
final Integer powerWakeMinute = this.powerWakeMinute;
if (powerWakeMinute == null) {
return null;
}
int delay = powerWakeMinute - minute;
if (!powerWakeHour.equals(hour)) {
// Simplify the algorithm, as the delta cannot be greater than 59 minutes.
delay += 60;
}
return delay;
}
public void setPowerWakeDelay(int minutes) {
final Integer hour = this.hour;
if (hour == null) {
return;
}
final Integer minute = this.minute;
if (minute == null) {
return;
}
LocalTime localTime = LocalTime.of(hour, minute);
localTime = localTime.plusMinutes(minutes);
this.powerWake = POWER_WAKE_ENABLED;
this.powerWakeHour = localTime.getHour();
this.powerWakeMinute = localTime.getMinute();
}
public void setPowerWakeDelayState(QuantityType<Time> time) {
setPowerWakeDelay(time.intValue());
}
public State getPowerWakeDelayState() {
final Integer delay = getPowerWakeDelay();
if (delay == null) {
return UnDefType.NULL;
}
return new QuantityType<>(delay, Units.MINUTE);
}
public State getSunriseBrightness() {
final Integer sunriseBrightness = this.sunriseBrightness;
if (sunriseBrightness == null) {
return UnDefType.NULL;
}
return new PercentType(sunriseBrightness * 4);
}
public void setSunriseBrightnessState(PercentType percent) {
sunriseBrightness = percent.intValue() / 4;
}
public State getSunriseDurationInMin() {
final Integer sunriseDurationInMin = this.sunriseDurationInMin;
if (sunriseDurationInMin == null) {
return UnDefType.NULL;
}
return new QuantityType<>(sunriseDurationInMin, Units.MINUTE);
}
public void setSunriseDurationState(QuantityType<Time> duration) {
sunriseDurationInMin = duration.intValue();
}
public State getSunriseSchema() {
final Integer sunriseSchema = this.sunriseSchema;
if (sunriseSchema == null) {
return UnDefType.NULL;
}
return new DecimalType(sunriseSchema);
}
public void setSunriseSchemaState(DecimalType schema) {
this.sunriseSchema = schema.intValue();
}
public State getSound() {
final String soundSource = this.soundSource;
if (soundSource == null) {
return UnDefType.NULL;
}
final String suffix = "off".equals(soundSource) ? "" : "-" + soundChannel;
return new StringType(soundSource + suffix);
}
public void setAlarmSoundState(StringType sound) {
final String[] values = sound.toFullString().split("-");
soundSource = values[0];
soundChannel = 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 setAlarmVolumeState(PercentType volume) {
soundVolume = volume.intValue() / 4;
}
public static AlarmSettingsData withDefaultValues(int position) {
final AlarmSettingsData data = new AlarmSettingsData();
data.position = position;
data.configured = false;
data.enabled = false;
data.hour = 7;
data.minute = 30;
data.powerWake = POWER_WAKE_DISABLED;
data.powerWakeHour = 0;
data.powerWakeMinute = 0;
data.sunriseSchema = 0;
data.sunriseBrightness = 20;
data.sunriseDurationInMin = 30;
data.repeatDay = 254;
data.soundSource = "wus";
data.soundChannel = "1";
data.soundVolume = 12;
return data;
}
}

View File

@ -0,0 +1,116 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.somneo.internal.model;
import java.time.Duration;
import java.time.LocalTime;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import com.google.gson.annotations.SerializedName;
/**
* This class represents the alarm state from the API.
*
* @author Michael Myrcik - Initial contribution
*/
public class AlarmStateData {
private static final int POWER_WAKE_ENABLED = 255;
@SerializedName("prfen")
private List<Boolean> enabled;
@SerializedName("prfvs")
private List<Boolean> configured;
@SerializedName("pwrsv")
private List<Integer> powerWake;
public @NonNull State getEnabledState(int position) {
final List<Boolean> enabled = this.enabled;
if (enabled == null) {
return UnDefType.NULL;
}
return OnOffType.from(enabled.get(position - 1));
}
public @NonNull State getConfiguredState(int position) {
final List<Boolean> configured = this.configured;
if (configured == null) {
return UnDefType.NULL;
}
return OnOffType.from(configured.get(position - 1));
}
public @NonNull State getPowerWakeState(int position) {
final List<Integer> powerWake = this.powerWake;
if (powerWake == null) {
return UnDefType.NULL;
}
position -= 1;
position *= 3;
return OnOffType.from((int) powerWake.get(position) == POWER_WAKE_ENABLED);
}
public @NonNull State getPowerWakeDelayState(int position, LocalTime alarmTime) {
final State powerWakeState = getPowerWakeState(position);
if (UnDefType.NULL.equals(powerWakeState)) {
return UnDefType.NULL;
}
if (OnOffType.OFF.equals(powerWakeState)) {
return QuantityType.valueOf(0, Units.MINUTE);
}
if (alarmTime == null) {
return UnDefType.NULL;
}
final LocalTime powerWakeTime = getPowerWakeTime(position);
if (powerWakeTime == null) {
return UnDefType.NULL;
}
Duration delay = Duration.between(alarmTime, powerWakeTime);
if (delay == null) {
return UnDefType.NULL;
}
return QuantityType.valueOf(delay.toMinutes(), Units.MINUTE);
}
private LocalTime getPowerWakeTime(int position) {
final List<Integer> powerWake = this.powerWake;
if (powerWake == null) {
return null;
}
position--;
position *= 3;
final Integer hour = powerWake.get(position + 1);
final Integer minute = powerWake.get(position + 2);
return LocalTime.of(hour, minute);
}
public int getAlarmCount() {
final List<Boolean> configured = this.configured;
if (configured == null) {
return 0;
}
return configured.size();
}
}

View File

@ -17,6 +17,8 @@ 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.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
@ -83,7 +85,7 @@ public class RelaxData {
if (durationInMin == null) {
return UnDefType.NULL;
}
return new DecimalType(durationInMin);
return new QuantityType<>(durationInMin, Units.MINUTE);
}
public void setDurationInMin(int durationInMin) {

View File

@ -17,7 +17,9 @@ 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.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
@ -87,7 +89,7 @@ public class SunsetData {
if (durationInMin == null) {
return UnDefType.NULL;
}
return new DecimalType(durationInMin);
return new QuantityType<>(durationInMin, Units.MINUTE);
}
public void setDurationInMin(int durationInMin) {

View File

@ -1,12 +1,31 @@
# add-on
addon.somneo.name = Somneo Binding
addon.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.somneo.hf367x.group.alarm1.label = Alarm 1
thing-type.somneo.hf367x.group.alarm10.label = Alarm 10
thing-type.somneo.hf367x.group.alarm11.label = Alarm 11
thing-type.somneo.hf367x.group.alarm12.label = Alarm 12
thing-type.somneo.hf367x.group.alarm13.label = Alarm 13
thing-type.somneo.hf367x.group.alarm14.label = Alarm 14
thing-type.somneo.hf367x.group.alarm15.label = Alarm 15
thing-type.somneo.hf367x.group.alarm16.label = Alarm 16
thing-type.somneo.hf367x.group.alarm2.label = Alarm 2
thing-type.somneo.hf367x.group.alarm3.label = Alarm 3
thing-type.somneo.hf367x.group.alarm4.label = Alarm 4
thing-type.somneo.hf367x.group.alarm5.label = Alarm 5
thing-type.somneo.hf367x.group.alarm6.label = Alarm 6
thing-type.somneo.hf367x.group.alarm7.label = Alarm 7
thing-type.somneo.hf367x.group.alarm8.label = Alarm 8
thing-type.somneo.hf367x.group.alarm9.label = Alarm 9
# thing types config
# 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
@ -15,8 +34,17 @@ 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.
thing-type.config.somneo.hf367x.refreshIntervalAlarmExtended.label = Refresh Interval for extended alarm clock settings
thing-type.config.somneo.hf367x.refreshIntervalAlarmExtended.description = Interval the device is polled in seconds. This polling triggers a sync icon on the device.
# channel group types
channel-group-type.somneo.alarm.label = Alarm Clock
channel-group-type.somneo.alarm.description = Channels to control alarm clock.
channel-group-type.somneo.alarmCommon.label = Common Alarm Clock Settings
channel-group-type.somneo.alarmCommon.description = Channels to control alarm clock settings that affect all alarm clocks.
channel-group-type.somneo.alarmErasable.label = Alarm Clock
channel-group-type.somneo.alarmErasable.description = Channels to control alarm clock.
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
@ -25,26 +53,46 @@ 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.sensor.label = Sensor Data
channel-group-type.somneo.sensor.description = Data from the various sensors.
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.lightIntensity.label = Sunset Brightness
channel-group-type.somneo.sunset.channel.lightIntensity.description = The channel allows to set the sunset 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.alarmConfigured.label = Configured
channel-type.somneo.alarmConfigured.description = Indicates if this alarm is configured. Deactivating this switch clears the alarm.
channel-type.somneo.alarmSnooze.label = Alarm Snooze
channel-type.somneo.alarmSnooze.description = The duration of the snooze function in minutes.
channel-type.somneo.alarmSound.label = Alarm Clock Sound
channel-type.somneo.alarmSound.description = The type of sound used for the alarm clock sound.
channel-type.somneo.alarmSound.state.option.off = No sound
channel-type.somneo.alarmSound.state.option.wus-1 = Forest Birds
channel-type.somneo.alarmSound.state.option.wus-2 = Summer Birds
channel-type.somneo.alarmSound.state.option.wus-3 = Buddha Wakeup
channel-type.somneo.alarmSound.state.option.wus-4 = Morning Alps
channel-type.somneo.alarmSound.state.option.wus-5 = Yoga Harmony
channel-type.somneo.alarmSound.state.option.wus-6 = Nepal Bowls
channel-type.somneo.alarmSound.state.option.wus-7 = Summer Lake
channel-type.somneo.alarmSound.state.option.wus-8 = Ocean Waves
channel-type.somneo.alarmSound.state.option.fmr-1 = FM 1
channel-type.somneo.alarmSound.state.option.fmr-2 = FM 2
channel-type.somneo.alarmSound.state.option.fmr-3 = FM 3
channel-type.somneo.alarmSound.state.option.fmr-4 = FM 4
channel-type.somneo.alarmSound.state.option.fmr-5 = FM 5
channel-type.somneo.alarmSwitch.label = Alarm Clock
channel-type.somneo.alarmSwitch.description = Turn the alarm clock on or off.
channel-type.somneo.alarmTime.label = Time
channel-type.somneo.alarmTime.description = Alarm clock time.
channel-type.somneo.alarmTime.state.pattern = %1$tH:%1$tM
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
@ -66,8 +114,10 @@ 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.brightness.label = Light Brightness
channel-type.somneo.brightness.description = The channel allows to set the light intensity.
channel-type.somneo.colorSchema.label = Color Schema
channel-type.somneo.colorSchema.description = Choose a personal sunset.
channel-type.somneo.colorSchema.description = Choose a personal sunrise or 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
@ -86,23 +136,49 @@ 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.powerWakeDelay.label = Power Wake Delay
channel-type.somneo.powerWakeDelay.description = How long after the normal alarm should Power Wake start.
channel-type.somneo.powerWakeSwitch.label = Power Wake
channel-type.somneo.powerWakeSwitch.description = Turn the power wake on or off.
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.relaxDuration.state.option.5\ min = 5 Minutes
channel-type.somneo.relaxDuration.state.option.10\ min = 10 Minutes
channel-type.somneo.relaxDuration.state.option.15\ min = 15 Minutes
channel-type.somneo.remainingTime.label = Remaining Time
channel-type.somneo.remainingTime.description = Remaining time from an activated program.
channel-type.somneo.remainingTime.state.pattern = %1$tM:%1$tS min
channel-type.somneo.repeatDay.label = Days
channel-type.somneo.repeatDay.description = The days on which the alarm is repeated.
channel-type.somneo.sunriseBrightness.label = Sunrise Brightness
channel-type.somneo.sunriseBrightness.description = The channel allows to set the sunrise light intensity.
channel-type.somneo.sunriseDuration.label = Sunrise Duration
channel-type.somneo.sunriseDuration.description = The duration of sunrise program in minutes.
channel-type.somneo.sunriseDuration.state.option.5\ min = 5 Minutes
channel-type.somneo.sunriseDuration.state.option.10\ min = 10 Minutes
channel-type.somneo.sunriseDuration.state.option.15\ min = 15 Minutes
channel-type.somneo.sunriseDuration.state.option.20\ min = 20 Minutes
channel-type.somneo.sunriseDuration.state.option.25\ min = 25 Minutes
channel-type.somneo.sunriseDuration.state.option.30\ min = 30 Minutes
channel-type.somneo.sunriseDuration.state.option.35\ min = 35 Minutes
channel-type.somneo.sunriseDuration.state.option.40\ min = 40 Minutes
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.sunsetDuration.state.option.5\ min = 5 Minutes
channel-type.somneo.sunsetDuration.state.option.10\ min = 10 Minutes
channel-type.somneo.sunsetDuration.state.option.15\ min = 15 Minutes
channel-type.somneo.sunsetDuration.state.option.20\ min = 20 Minutes
channel-type.somneo.sunsetDuration.state.option.30\ min = 30 Minutes
channel-type.somneo.sunsetDuration.state.option.45\ min = 45 Minutes
channel-type.somneo.sunsetDuration.state.option.60\ min = 60 Minutes
channel-type.somneo.temperature.label = Temperature
channel-type.somneo.temperature.description = The current temperature in °C.
# channel group types
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.channel.volume.label = Volume
channel-group-type.somneo.sunset.channel.volume.description = Set the volume of the sunset program.

View File

@ -14,8 +14,62 @@
<channel-group id="sunset" typeId="sunset"/>
<channel-group id="relax" typeId="relax"/>
<channel-group id="audio" typeId="audio"/>
<channel-group id="alarm" typeId="alarmCommon"/>
<channel-group id="alarm1" typeId="alarm">
<label>Alarm 1</label>
</channel-group>
<channel-group id="alarm2" typeId="alarm">
<label>Alarm 2</label>
</channel-group>
<channel-group id="alarm3" typeId="alarmErasable">
<label>Alarm 3</label>
</channel-group>
<channel-group id="alarm4" typeId="alarmErasable">
<label>Alarm 4</label>
</channel-group>
<channel-group id="alarm5" typeId="alarmErasable">
<label>Alarm 5</label>
</channel-group>
<channel-group id="alarm6" typeId="alarmErasable">
<label>Alarm 6</label>
</channel-group>
<channel-group id="alarm7" typeId="alarmErasable">
<label>Alarm 7</label>
</channel-group>
<channel-group id="alarm8" typeId="alarmErasable">
<label>Alarm 8</label>
</channel-group>
<channel-group id="alarm9" typeId="alarmErasable">
<label>Alarm 9</label>
</channel-group>
<channel-group id="alarm10" typeId="alarmErasable">
<label>Alarm 10</label>
</channel-group>
<channel-group id="alarm11" typeId="alarmErasable">
<label>Alarm 11</label>
</channel-group>
<channel-group id="alarm12" typeId="alarmErasable">
<label>Alarm 12</label>
</channel-group>
<channel-group id="alarm13" typeId="alarmErasable">
<label>Alarm 13</label>
</channel-group>
<channel-group id="alarm14" typeId="alarmErasable">
<label>Alarm 14</label>
</channel-group>
<channel-group id="alarm15" typeId="alarmErasable">
<label>Alarm 15</label>
</channel-group>
<channel-group id="alarm16" typeId="alarmErasable">
<label>Alarm 16</label>
</channel-group>
</channel-groups>
<properties>
<property name="thingTypeVersion">1</property>
<property name="vendor">Philips</property>
</properties>
<config-description>
<parameter name="hostname" type="text" required="true">
<context>network-address</context>
@ -34,6 +88,12 @@
<default>30</default>
<advanced>true</advanced>
</parameter>
<parameter name="refreshIntervalAlarmExtended" type="integer" unit="s" min="1">
<label>Refresh Interval for extended alarm clock settings</label>
<description>Interval the device is polled in seconds. This polling triggers a sync icon on the device.</description>
<default>300</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>
@ -43,6 +103,52 @@
</config-description>
</thing-type>
<channel-group-type id="alarm">
<label>Alarm Clock</label>
<description>Channels to control alarm clock.</description>
<category>Alarm</category>
<channels>
<channel id="switch" typeId="alarmSwitch"/>
<channel id="repeatDay" typeId="repeatDay"/>
<channel id="alarmTime" typeId="alarmTime"/>
<channel id="powerWake" typeId="powerWakeSwitch"/>
<channel id="powerWakeDelay" typeId="powerWakeDelay"/>
<channel id="sunriseDuration" typeId="sunriseDuration"/>
<channel id="sunriseBrightness" typeId="sunriseBrightness"/>
<channel id="sunriseSchema" typeId="colorSchema"/>
<channel id="sound" typeId="alarmSound"/>
<channel id="volume" typeId="system.volume"/>
</channels>
</channel-group-type>
<channel-group-type id="alarmCommon">
<label>Common Alarm Clock Settings</label>
<description>Channels to control alarm clock settings that affect all alarm clocks.</description>
<category>Alarm</category>
<channels>
<channel id="snooze" typeId="alarmSnooze"/>
</channels>
</channel-group-type>
<channel-group-type id="alarmErasable">
<label>Alarm Clock</label>
<description>Channels to control alarm clock.</description>
<category>Alarm</category>
<channels>
<channel id="configured" typeId="alarmConfigured"/>
<channel id="switch" typeId="alarmSwitch"/>
<channel id="repeatDay" typeId="repeatDay"/>
<channel id="alarmTime" typeId="alarmTime"/>
<channel id="powerWake" typeId="powerWakeSwitch"/>
<channel id="powerWakeDelay" typeId="powerWakeDelay"/>
<channel id="sunriseDuration" typeId="sunriseDuration"/>
<channel id="sunriseBrightness" typeId="sunriseBrightness"/>
<channel id="sunriseSchema" typeId="colorSchema"/>
<channel id="sound" typeId="alarmSound"/>
<channel id="volume" typeId="system.volume"/>
</channels>
</channel-group-type>
<channel-group-type id="audio">
<label>Audio Player</label>
<description>Channels to control the audio player.</description>
@ -67,7 +173,7 @@
<description>Different light channels.</description>
<category>Light</category>
<channels>
<channel id="main" typeId="system.brightness"/>
<channel id="main" typeId="brightness"/>
<channel id="night" typeId="light"/>
</channels>
</channel-group-type>
@ -96,10 +202,7 @@
<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="lightIntensity" typeId="sunriseBrightness"/>
<channel id="volume" typeId="system.volume"/>
</channels>
</channel-group-type>
@ -114,9 +217,9 @@
<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 id="lightIntensity" typeId="brightness">
<label>Sunset Brightness</label>
<description>The channel allows to set the sunset light intensity.</description>
</channel>
<channel id="duration" typeId="sunsetDuration"/>
<channel id="colorSchema" typeId="colorSchema"/>
@ -125,6 +228,80 @@
</channels>
</channel-group-type>
<channel-type id="alarmConfigured">
<item-type>Switch</item-type>
<label>Configured</label>
<description>Indicates if this alarm is configured. Deactivating this switch clears the alarm.</description>
<category>Switch</category>
<tags>
<tag>Control</tag>
</tags>
</channel-type>
<channel-type id="alarmSnooze">
<item-type>Number:Time</item-type>
<label>Alarm Snooze</label>
<description>The duration of the snooze function in minutes.</description>
<category>Time</category>
<tags>
<tag>Control</tag>
<tag>Duration</tag>
</tags>
<state pattern="%d min" min="1" max="20" step="1"/>
</channel-type>
<channel-type id="alarmSound">
<item-type>String</item-type>
<label>Alarm Clock Sound</label>
<description>The type of sound used for the alarm clock sound.</description>
<category>Noise</category>
<tags>
<tag>Control</tag>
<tag>Noise</tag>
</tags>
<state>
<options>
<option value="off">No sound</option>
<option value="wus-1">Forest Birds</option>
<option value="wus-2">Summer Birds</option>
<option value="wus-3">Buddha Wakeup</option>
<option value="wus-4">Morning Alps</option>
<option value="wus-5">Yoga Harmony</option>
<option value="wus-6">Nepal Bowls</option>
<option value="wus-7">Summer Lake</option>
<option value="wus-8">Ocean Waves</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="alarmSwitch">
<item-type>Switch</item-type>
<label>Alarm Clock</label>
<description>Turn the alarm clock on or off.</description>
<category>Switch</category>
<tags>
<tag>Control</tag>
<tag>Power</tag>
</tags>
</channel-type>
<channel-type id="alarmTime">
<item-type>DateTime</item-type>
<label>Time</label>
<description>Alarm clock time.</description>
<category>Time</category>
<tags>
<tag>Control</tag>
<tag>Timestamp</tag>
</tags>
<state pattern="%1$tH:%1$tM"/>
</channel-type>
<channel-type id="ambientNoise">
<item-type>String</item-type>
<label>Ambient Noise</label>
@ -170,10 +347,22 @@
</state>
</channel-type>
<channel-type id="brightness">
<item-type>Dimmer</item-type>
<label>Light Brightness</label>
<description>The channel allows to set the light intensity.</description>
<category>Light</category>
<tags>
<tag>Control</tag>
<tag>Light</tag>
</tags>
<state pattern="%d %%" min="0" max="100" step="4"/>
</channel-type>
<channel-type id="colorSchema">
<item-type>Number</item-type>
<label>Color Schema</label>
<description>Choose a personal sunset.</description>
<description>Choose a personal sunrise or sunset.</description>
<category>Sunset</category>
<tags>
<tag>Control</tag>
@ -261,6 +450,29 @@
<state pattern="%d %unit%" readOnly="true"/>
</channel-type>
<channel-type id="powerWakeDelay">
<item-type>Number:Time</item-type>
<label>Power Wake Delay</label>
<description>How long after the normal alarm should Power Wake start.</description>
<category>Time</category>
<tags>
<tag>Control</tag>
<tag>Duration</tag>
</tags>
<state pattern="%d min" min="0" max="59" step="1"/>
</channel-type>
<channel-type id="powerWakeSwitch">
<item-type>Switch</item-type>
<label>Power Wake</label>
<description>Turn the power wake on or off.</description>
<category>Switch</category>
<tags>
<tag>Control</tag>
<tag>Power</tag>
</tags>
</channel-type>
<channel-type id="preset">
<item-type>String</item-type>
<label>FM Preset</label>
@ -282,9 +494,9 @@
</tags>
<state>
<options>
<option value="5">5 Minutes</option>
<option value="10">10 Minutes</option>
<option value="15">15 Minutes</option>
<option value="5 min">5 Minutes</option>
<option value="10 min">10 Minutes</option>
<option value="15 min">15 Minutes</option>
</options>
</state>
</channel-type>
@ -301,6 +513,52 @@
<state pattern="%1$tM:%1$tS min" readOnly="true"/>
</channel-type>
<channel-type id="repeatDay">
<item-type>Number</item-type>
<label>Days</label>
<description>The days on which the alarm is repeated.</description>
<category>Calendar</category>
<tags>
<tag>Control</tag>
<tag>Timestamp</tag>
</tags>
</channel-type>
<channel-type id="sunriseBrightness">
<item-type>Dimmer</item-type>
<label>Sunrise Brightness</label>
<description>The channel allows to set the sunrise light intensity.</description>
<category>Light</category>
<tags>
<tag>Control</tag>
<tag>Light</tag>
</tags>
<state pattern="%d %%" min="4" max="100" step="4"/>
</channel-type>
<channel-type id="sunriseDuration">
<item-type>Number:Time</item-type>
<label>Sunrise Duration</label>
<description>The duration of sunrise program in minutes.</description>
<category>Time</category>
<tags>
<tag>Control</tag>
<tag>Duration</tag>
</tags>
<state>
<options>
<option value="5 min">5 Minutes</option>
<option value="10 min">10 Minutes</option>
<option value="15 min">15 Minutes</option>
<option value="20 min">20 Minutes</option>
<option value="25 min">25 Minutes</option>
<option value="30 min">30 Minutes</option>
<option value="35 min">35 Minutes</option>
<option value="40 min">40 Minutes</option>
</options>
</state>
</channel-type>
<channel-type id="sunsetDuration">
<item-type>Number:Time</item-type>
<label>Duration</label>
@ -312,13 +570,13 @@
</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>
<option value="5 min">5 Minutes</option>
<option value="10 min">10 Minutes</option>
<option value="15 min">15 Minutes</option>
<option value="20 min">20 Minutes</option>
<option value="30 min">30 Minutes</option>
<option value="45 min">45 Minutes</option>
<option value="60 min">60 Minutes</option>
</options>
</state>
</channel-type>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
<thing-type uid="somneo:hf367x">
<instruction-set targetVersion="1">
<update-channel id="main" groupIds="light">
<type>somneo:brightness</type>
</update-channel>
<update-channel id="lightIntensity" groupIds="sunset">
<type>somneo:brightness</type>
</update-channel>
<update-channel id="lightIntensity" groupIds="relax">
<type>somneo:sunriseBrightness</type>
</update-channel>
<add-channel id="snooze" groupIds="alarm">
<type>somneo:alarmSnooze</type>
</add-channel>
<add-channel id="configured"
groupIds="alarm3,alarm4,alarm5,alarm6,alarm7,alarm8,alarm9,alarm10,alarm11,alarm12,alarm13,alarm14,alarm15,alarm16">
<type>somneo:alarmConfigured</type>
</add-channel>
<add-channel id="switch"
groupIds="alarm1,alarm2,alarm3,alarm4,alarm5,alarm6,alarm7,alarm8,alarm9,alarm10,alarm11,alarm12,alarm13,alarm14,alarm15,alarm16">
<type>somneo:alarmSwitch</type>
</add-channel>
<add-channel id="repeatDay"
groupIds="alarm1,alarm2,alarm3,alarm4,alarm5,alarm6,alarm7,alarm8,alarm9,alarm10,alarm11,alarm12,alarm13,alarm14,alarm15,alarm16">
<type>somneo:repeatDay</type>
</add-channel>
<add-channel id="alarmTime"
groupIds="alarm1,alarm2,alarm3,alarm4,alarm5,alarm6,alarm7,alarm8,alarm9,alarm10,alarm11,alarm12,alarm13,alarm14,alarm15,alarm16">
<type>somneo:alarmTime</type>
</add-channel>
<add-channel id="powerWake"
groupIds="alarm1,alarm2,alarm3,alarm4,alarm5,alarm6,alarm7,alarm8,alarm9,alarm10,alarm11,alarm12,alarm13,alarm14,alarm15,alarm16">
<type>somneo:powerWakeSwitch</type>
</add-channel>
<add-channel id="powerWakeDelay"
groupIds="alarm1,alarm2,alarm3,alarm4,alarm5,alarm6,alarm7,alarm8,alarm9,alarm10,alarm11,alarm12,alarm13,alarm14,alarm15,alarm16">
<type>somneo:powerWakeDelay</type>
</add-channel>
<add-channel id="sunriseDuration"
groupIds="alarm1,alarm2,alarm3,alarm4,alarm5,alarm6,alarm7,alarm8,alarm9,alarm10,alarm11,alarm12,alarm13,alarm14,alarm15,alarm16">
<type>somneo:sunriseDuration</type>
</add-channel>
<add-channel id="sunriseBrightness"
groupIds="alarm1,alarm2,alarm3,alarm4,alarm5,alarm6,alarm7,alarm8,alarm9,alarm10,alarm11,alarm12,alarm13,alarm14,alarm15,alarm16">
<type>somneo:sunriseBrightness</type>
</add-channel>
<add-channel id="sunriseSchema"
groupIds="alarm1,alarm2,alarm3,alarm4,alarm5,alarm6,alarm7,alarm8,alarm9,alarm10,alarm11,alarm12,alarm13,alarm14,alarm15,alarm16">
<type>somneo:colorSchema</type>
</add-channel>
<add-channel id="sound"
groupIds="alarm1,alarm2,alarm3,alarm4,alarm5,alarm6,alarm7,alarm8,alarm9,alarm10,alarm11,alarm12,alarm13,alarm14,alarm15,alarm16">
<type>somneo:alarmSound</type>
</add-channel>
<add-channel id="volume"
groupIds="alarm1,alarm2,alarm3,alarm4,alarm5,alarm6,alarm7,alarm8,alarm9,alarm10,alarm11,alarm12,alarm13,alarm14,alarm15,alarm16">
<type>system:volume</type>
</add-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>