From 55643a1eda261ea54f21da739ecf6a10f696056f Mon Sep 17 00:00:00 2001 From: David Geisler Date: Fri, 25 Nov 2022 00:53:25 +0100 Subject: [PATCH] add scene device trigger --- .../iot/dirigera/client/api/SceneApi.java | 6 +- .../dirigera/client/api/http/ClientApi.java | 6 +- .../client/api/model/scene/SceneTrigger.java | 9 +- .../api/model/scene/SceneTriggerApp.java | 9 +- .../model/scene/SceneTriggerController.java | 20 +++ .../scene/SceneTriggerControllerTrigger.java | 24 +++ .../scene/SceneTriggerSunriseSunset.java | 9 +- .../api/model/scene/SceneTriggerTime.java | 8 +- .../examples/scenetriggers/SceneTriggers.java | 151 ++++++++++++++++++ 9 files changed, 220 insertions(+), 22 deletions(-) create mode 100644 dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerController.java create mode 100644 dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerControllerTrigger.java create mode 100644 dirigera-client-examples/src/main/java/de/dvdgeisler/iot/dirigera/client/examples/scenetriggers/SceneTriggers.java diff --git a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/SceneApi.java b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/SceneApi.java index 0e8e24a..f8f2496 100644 --- a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/SceneApi.java +++ b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/SceneApi.java @@ -27,7 +27,7 @@ public Mono refresh(final Scene scene) { public Mono create(final String name, final String icon) { return this.clientApi.scene.createScene(new SceneAttributes( new SceneInfo(name, icon), List.of(), List.of())) - .flatMap(id -> this.clientApi.scene.getScene(id.id)); + .flatMap(id -> this.clientApi.scene.getScene(id.id).retry(10)); } public Mono delete(final Scene scene) { @@ -40,8 +40,8 @@ public Mono update(final Scene scene, final String name, final String ico .flatMap(this::refresh); } - public Mono setTrigger(final Scene scene, final List trigger) { - return this.clientApi.scene.updateScene(scene.id, new SceneAttributes(null, trigger, List.of())) + public Mono setTrigger(final Scene scene, final List triggers) { + return this.clientApi.scene.updateScene(scene.id, new SceneAttributes(scene.attributes.info, triggers, List.of())) .thenReturn(scene) .flatMap(this::refresh); } diff --git a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/http/ClientApi.java b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/http/ClientApi.java index 32eeeeb..fc88093 100644 --- a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/http/ClientApi.java +++ b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/http/ClientApi.java @@ -89,7 +89,7 @@ public Mono dump() { .bodyToMono(Map.class); } - public void websocket(final Consumer consumer) { + public Mono websocket(final Consumer consumer) { final String token; final String authorizationHeader; final HttpClient httpClient; @@ -104,13 +104,13 @@ public void websocket(final Consumer consumer) { .headers(headers -> headers.add(HttpHeaders.AUTHORIZATION, authorizationHeader)) .keepAlive(true); client = new ReactorNettyWebSocketClient(httpClient); - client.execute(URI.create(String.format("https://%s:%d/v1/", this.hostname, this.port)), session -> + return client.execute(URI.create(String.format("https://%s:%d/v1/", this.hostname, this.port)), session -> session.receive() .map(WebSocketMessage::getPayloadAsText) .doOnNext(consumer) .repeat() .then() - ).block(); + ); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTrigger.java b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTrigger.java index db53812..257cbd8 100644 --- a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTrigger.java +++ b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTrigger.java @@ -4,22 +4,29 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import de.dvdgeisler.iot.dirigera.client.api.model.Identifier; +import java.time.LocalDateTime; + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true) @JsonSubTypes({ @JsonSubTypes.Type(value = SceneTriggerApp.class, name = "app"), @JsonSubTypes.Type(value = SceneTriggerSunriseSunset.class, name = "sunriseSunset"), @JsonSubTypes.Type(value = SceneTriggerTime.class, name = "time"), + @JsonSubTypes.Type(value = SceneTriggerController.class, name = "controller"), }) public class SceneTrigger extends Identifier { public SceneTriggerType type; public Boolean disabled; + public LocalDateTime triggeredAt; + public SceneEndTrigger endTrigger; public SceneTrigger() { } - public SceneTrigger(final String id, final SceneTriggerType type, final Boolean disabled) { + public SceneTrigger(final String id, final SceneTriggerType type, final Boolean disabled, final LocalDateTime triggeredAt, final SceneEndTrigger endTrigger) { super(id); this.type = type; this.disabled = disabled; + this.triggeredAt = triggeredAt; + this.endTrigger = endTrigger; } } diff --git a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerApp.java b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerApp.java index d3a2c0d..8ae6997 100644 --- a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerApp.java +++ b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerApp.java @@ -1,17 +1,18 @@ package de.dvdgeisler.iot.dirigera.client.api.model.scene; +import com.fasterxml.jackson.annotation.JsonInclude; + import java.time.LocalDateTime; import static de.dvdgeisler.iot.dirigera.client.api.model.scene.SceneTriggerType.APPLICATION; +@JsonInclude(JsonInclude.Include.NON_NULL) public class SceneTriggerApp extends SceneTrigger { - public LocalDateTime triggeredAt; public SceneTriggerApp() { } - public SceneTriggerApp(final String id, final Boolean disabled, final LocalDateTime triggeredAt) { - super(id, APPLICATION, disabled); - this.triggeredAt = triggeredAt; + public SceneTriggerApp(final String id, final Boolean disabled, final LocalDateTime triggeredAt, final SceneEndTrigger endTrigger) { + super(id, APPLICATION, disabled, triggeredAt, endTrigger); } } diff --git a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerController.java b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerController.java new file mode 100644 index 0000000..8d6891a --- /dev/null +++ b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerController.java @@ -0,0 +1,20 @@ +package de.dvdgeisler.iot.dirigera.client.api.model.scene; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.time.LocalDateTime; + +import static de.dvdgeisler.iot.dirigera.client.api.model.scene.SceneTriggerType.CONTROLLER; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class SceneTriggerController extends SceneTrigger { + public SceneTriggerControllerTrigger trigger; + + public SceneTriggerController() { + } + + public SceneTriggerController(final String id, final Boolean disabled, final LocalDateTime triggeredAt, final SceneEndTrigger endTrigger, final SceneTriggerControllerTrigger trigger) { + super(id, CONTROLLER, disabled, triggeredAt, endTrigger); + this.trigger = trigger; + } +} diff --git a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerControllerTrigger.java b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerControllerTrigger.java new file mode 100644 index 0000000..4899af3 --- /dev/null +++ b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerControllerTrigger.java @@ -0,0 +1,24 @@ +package de.dvdgeisler.iot.dirigera.client.api.model.scene; + +import com.fasterxml.jackson.annotation.JsonInclude; +import de.dvdgeisler.iot.dirigera.client.api.model.device.DeviceType; + +import java.util.List; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class SceneTriggerControllerTrigger { + public List days; + public DeviceType controllerType; + public Integer buttonIndex; + public String deviceId; + + public SceneTriggerControllerTrigger(final List days, final DeviceType controllerType, final Integer buttonIndex, final String deviceId) { + this.days = days; + this.controllerType = controllerType; + this.buttonIndex = buttonIndex; + this.deviceId = deviceId; + } + + public SceneTriggerControllerTrigger() { + } +} diff --git a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerSunriseSunset.java b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerSunriseSunset.java index f80ce63..5f24107 100644 --- a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerSunriseSunset.java +++ b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerSunriseSunset.java @@ -1,21 +1,20 @@ package de.dvdgeisler.iot.dirigera.client.api.model.scene; +import com.fasterxml.jackson.annotation.JsonInclude; + import java.time.LocalDateTime; import static de.dvdgeisler.iot.dirigera.client.api.model.scene.SceneTriggerType.SUNRISE_SUNSET; +@JsonInclude(JsonInclude.Include.NON_NULL) public class SceneTriggerSunriseSunset extends SceneTrigger { public SceneTriggerSunriseSunsetTrigger trigger; public LocalDateTime nextTriggerAt; - public LocalDateTime triggeredAt; - public SceneEndTrigger endTrigger; public SceneTriggerSunriseSunset(final String id, final Boolean disabled, final SceneEndTrigger endTrigger, final SceneTriggerSunriseSunsetTrigger trigger, final LocalDateTime nextTriggerAt, final LocalDateTime triggeredAt) { - super(id, SUNRISE_SUNSET, disabled); + super(id, SUNRISE_SUNSET, disabled, triggeredAt, endTrigger); this.trigger = trigger; this.nextTriggerAt = nextTriggerAt; - this.triggeredAt = triggeredAt; - this.endTrigger = endTrigger; } public SceneTriggerSunriseSunset() { diff --git a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerTime.java b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerTime.java index 5e83d08..bc1e3b0 100644 --- a/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerTime.java +++ b/dirigera-client-api/src/main/java/de/dvdgeisler/iot/dirigera/client/api/model/scene/SceneTriggerTime.java @@ -7,15 +7,11 @@ public class SceneTriggerTime extends SceneTrigger { public SceneTriggerTimeTrigger trigger; public LocalDateTime nextTriggerAt; - public LocalDateTime triggeredAt; - public SceneEndTrigger endTrigger; - public SceneTriggerTime(final String id, final SceneTriggerType type, final Boolean disabled, final SceneEndTrigger endTrigger, final SceneTriggerTimeTrigger trigger, final LocalDateTime nextTriggerAt, final LocalDateTime triggeredAt) { - super(id, TIME, disabled); + public SceneTriggerTime(final String id, final Boolean disabled, final SceneEndTrigger endTrigger, final SceneTriggerTimeTrigger trigger, final LocalDateTime nextTriggerAt, final LocalDateTime triggeredAt) { + super(id, TIME, disabled, triggeredAt, endTrigger); this.trigger = trigger; this.nextTriggerAt = nextTriggerAt; - this.triggeredAt = triggeredAt; - this.endTrigger = endTrigger; } public SceneTriggerTime() { diff --git a/dirigera-client-examples/src/main/java/de/dvdgeisler/iot/dirigera/client/examples/scenetriggers/SceneTriggers.java b/dirigera-client-examples/src/main/java/de/dvdgeisler/iot/dirigera/client/examples/scenetriggers/SceneTriggers.java new file mode 100644 index 0000000..0296799 --- /dev/null +++ b/dirigera-client-examples/src/main/java/de/dvdgeisler/iot/dirigera/client/examples/scenetriggers/SceneTriggers.java @@ -0,0 +1,151 @@ +package de.dvdgeisler.iot.dirigera.client.examples.scenetriggers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import de.dvdgeisler.iot.dirigera.client.api.DirigeraApi; +import de.dvdgeisler.iot.dirigera.client.api.http.ClientApi; +import de.dvdgeisler.iot.dirigera.client.api.model.device.Device; +import de.dvdgeisler.iot.dirigera.client.api.model.device.DeviceType; +import de.dvdgeisler.iot.dirigera.client.api.model.scene.Scene; +import de.dvdgeisler.iot.dirigera.client.api.model.scene.SceneTriggerApp; +import de.dvdgeisler.iot.dirigera.client.api.model.scene.SceneTriggerController; +import de.dvdgeisler.iot.dirigera.client.api.model.scene.SceneTriggerControllerTrigger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import reactor.core.publisher.Mono; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * + */ +@SpringBootApplication +@ComponentScan(basePackageClasses = {DirigeraApi.class}) +public class SceneTriggers { + private final static Logger log = LoggerFactory.getLogger(SceneTriggers.class); + private final DirigeraApi dapi; + private final ClientApi capi; + private final ObjectMapper json; + + public SceneTriggers(final DirigeraApi dapi, final ClientApi capi, final ObjectMapper json) { + this.dapi = dapi; + this.capi = capi; + this.json = json; + } + + private static SceneTriggerController createTrigger(final Device device, final int buttonIndex) { + return new SceneTriggerController( // trigger for button 0 (turn light on) + null, false, null, null, + new SceneTriggerControllerTrigger( + null, + DeviceType.SHORTCUT_CONTROLLER, + buttonIndex, + device.id)); + } + + private Scene createDummyScene(final Device device, final int button) { + final Scene scene; + final String name; + + name = String.format("%s button %d", device.attributes.state.customName, button); + + scene = this.dapi.scene.create(name, "Icon") + .doOnSuccess(s -> log.info("Created Scene {}: name={}, icon={}", s.id, s.attributes.info.name, s.attributes.info.icon)) + .block(); // create dummy scene + + return this.dapi.scene.setTrigger(scene, List.of( + new SceneTriggerApp(null, null, null, null), + createTrigger(device, button)) + ).block(); + } + + private Stream createDummyScenes(final Device device) { + return Stream.of( + this.createDummyScene(device, 0), + this.createDummyScene(device, 1), + this.createDummyScene(device, 2)); + } + + @Bean + public CommandLineRunner run() { + return (String... args) -> { + List scenes; + this.dapi.pairIfRequired().block(); + + + scenes = new ArrayList<>(); + + + try { + this.dapi.device.controller.light // create dummy scenes for light controller + .all() + .block() + .stream() + .flatMap(this::createDummyScenes) + .forEach(scenes::add); + this.dapi.device.controller.shortcut // create dummy scenes for shortcut controller + .all() + .block() + .stream() + .flatMap(this::createDummyScenes) + .forEach(scenes::add); + this.dapi.device.controller.sound // create dummy scenes for sound controller + .all() + .block() + .stream() + .flatMap(this::createDummyScenes) + .forEach(scenes::add); + this.dapi.device.motionSensor // create dummy scenes for motion sensors + .all() + .block() + .stream() + .flatMap(this::createDummyScenes) + .forEach(scenes::add); + log.info("Press button of any connected controller"); + this.capi.websocket(this::logButtonPress) + .block(Duration.ofSeconds(60)); + } catch (Exception e) { + log.error(e.getMessage()); + } + + scenes.stream() + .peek(scene -> log.info("Delete scene {}", scene.attributes.info.name)) + .map(this.dapi.scene::delete) + .forEach(Mono::block); + }; + } + + private void logButtonPress(final String s) { + final Map json, data, info; + final String type; + + try { + json = this.json.readValue(s, Map.class); + type = json.get("type").toString(); + if(!Objects.equals(type, "sceneUpdated")) { + log.info("Received: {}", s); + return; + } + data = (Map) json.get("data"); + info = (Map) data.get("info"); + log.info("Received: {}", info.get("name")); + } catch (Throwable e) { + log.error(e.getMessage()); + } + } + + public static void main(String[] args) { + SpringApplication.run(SceneTriggers.class, args).close(); + } + + +}