]> git.basschouten.com Git - openhab-addons.git/commitdiff
[somneo] Add alarm support and other improvements (#14882)
author0x4d4d <20473294+0x4d4d@users.noreply.github.com>
Sat, 17 Jun 2023 10:55:27 +0000 (12:55 +0200)
committerGitHub <noreply@github.com>
Sat, 17 Jun 2023 10:55:27 +0000 (12:55 +0200)
* [somneo] Add alarm clock channels

Signed-off-by: Michael Myrcik <michael.myrcik@web.de>
13 files changed:
bundles/org.openhab.binding.somneo/README.md
bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoBindingConstants.java
bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoConfiguration.java
bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoHandler.java
bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/SomneoHttpConnector.java
bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/model/AlarmSchedulesData.java [new file with mode: 0644]
bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/model/AlarmSettingsData.java [new file with mode: 0644]
bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/model/AlarmStateData.java [new file with mode: 0644]
bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/model/RelaxData.java
bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/model/SunsetData.java
bundles/org.openhab.binding.somneo/src/main/resources/OH-INF/i18n/somneo.properties
bundles/org.openhab.binding.somneo/src/main/resources/OH-INF/thing/thing-types.xml
bundles/org.openhab.binding.somneo/src/main/resources/OH-INF/update/update.xml [new file with mode: 0644]

index 96887d9bc0c08b0161b5d00e083e4542c3d62f04..f2da92dc0ea4fa28b76fe9cad3e9114cbb36c026 100644 (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
+        }
+    }
 }
 ```
 
index 4317f5301610f3cfc84ce1609ed74daac486a9d8..7998b0c7af932e4a564857aee7163f3b3b43d4c2 100644 (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";
 }
index 8c16e8374ba706feefb47c4f017d1d6eb1a40ae8..6ade29b15ded456bd98f33ce80d57b1c88594328 100644 (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;
 }
index 1530ea2d4f7dd1163537c2aab5c83c9e6c42649f..c56893043ae32ce6247232f26f2d486dda5f6c3e 100644 (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;
-        }
+        try {
+            final SomneoHttpConnector connector = this.connector;
+            if (connector == null) {
+                return;
+            }
 
-        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;
+            }
 
-        try {
             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());
-
-            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());
+            updateSensors();
+
+            updateLights();
+
+            updateSunset();
+
+            updateRelax();
+
+            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;
+    }
 }
index 9c7b14d9aa6a6e8c09b90b21b6bdb458ae5a9784..c01378f4b0e3fc1b4a9ac3c5f43f2b97cbdc0ab0 100644 (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,13 +307,165 @@ public class SomneoHttpConnector {
         return executeUrl("GET", PRESET_ENDPOINT, PresetData.class);
     }
 
-    private <T> T executeUrl(String httpMethod, String endpoint, Class<T> classOfT)
+    public AlarmStateData fetchAlarmStateData() throws TimeoutException, InterruptedException, ExecutionException {
+        return executeUrl("GET", ALARM_STATES_ENDPOINT, AlarmStateData.class);
+    }
+
+    public AlarmSchedulesData fetchAlarmScheduleData()
             throws TimeoutException, InterruptedException, ExecutionException {
-        final String responseBody = executeUrl("GET", endpoint, (String) null);
-        final T data = gson.fromJson(responseBody, classOfT);
+        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(httpMethod, endpoint, (String) null);
+        return gson.fromJson(responseBody, classOfT);
+    }
+
     private void executeUrl(String httpMethod, String endpoint, Object data)
             throws TimeoutException, InterruptedException, ExecutionException {
         final String content = gson.toJson(data);
diff --git a/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/model/AlarmSchedulesData.java b/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/model/AlarmSchedulesData.java
new file mode 100644 (file)
index 0000000..e193434
--- /dev/null
@@ -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);
+    }
+}
diff --git a/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/model/AlarmSettingsData.java b/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/model/AlarmSettingsData.java
new file mode 100644 (file)
index 0000000..fa588b0
--- /dev/null
@@ -0,0 +1,352 @@
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.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;
+    }
+}
diff --git a/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/model/AlarmStateData.java b/bundles/org.openhab.binding.somneo/src/main/java/org/openhab/binding/somneo/internal/model/AlarmStateData.java
new file mode 100644 (file)
index 0000000..e8fd695
--- /dev/null
@@ -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();
+    }
+}
index 0352191ce59ea9511f8faad0c1ece622fb6101da..7003737e2a22cf7aad0bdad39caacf3d40152178 100644 (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) {
index 7b851d721f1b17c4e8d757e5efc43b24693fe9d3..84a1df0f84a1a3c1b8df90a6def918691edb677d 100644 (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) {
index 10ab0b8ac337cbc4da4803df38ea1ef325be2fd2..2347575299913e2f84bb46438fdfbf409176c160 100644 (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.
index 56a20c41db2f2825dc37b05c539bbe011da2dd00..ed33807b55f24f2c8103557f72fccf7f4dc8ba62 100644 (file)
                        <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>
                                <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>
                </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>
                <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>
                        <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>
                                <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"/>
                </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>
                </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>
                <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>
                </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>
                <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>
                </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>
diff --git a/bundles/org.openhab.binding.somneo/src/main/resources/OH-INF/update/update.xml b/bundles/org.openhab.binding.somneo/src/main/resources/OH-INF/update/update.xml
new file mode 100644 (file)
index 0000000..37f7742
--- /dev/null
@@ -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>