Skip to content

Commit

Permalink
[somneo] Add alarm support and other improvements (openhab#14882)
Browse files Browse the repository at this point in the history
* [somneo] Add alarm clock channels

Signed-off-by: Michael Myrcik <[email protected]>
Signed-off-by: Jørgen Austvik <[email protected]>
  • Loading branch information
myrck authored and austvik committed Mar 27, 2024
1 parent 75f06e8 commit bfc2001
Show file tree
Hide file tree
Showing 13 changed files with 1,737 additions and 155 deletions.
219 changes: 181 additions & 38 deletions bundles/org.openhab.binding.somneo/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@

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;
Expand All @@ -28,6 +31,9 @@
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;
Expand All @@ -40,10 +46,21 @@
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.
Expand Down Expand Up @@ -290,13 +307,165 @@ public PresetData fetchPresetData() throws TimeoutException, InterruptedExceptio
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit bfc2001

Please sign in to comment.