Skip to content

Commit

Permalink
Merge pull request #57 from dvdgeisler/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
dvdgeisler authored Dec 5, 2022
2 parents 4d5774b + e16bead commit 2d80173
Show file tree
Hide file tree
Showing 27 changed files with 1,066 additions and 976 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,30 @@ barely tested, and some are known as inoperable.
* TRADFRI signal repeater
* Light & Driver
* STOFTMOLN ceiling/wall lamp WW24
* FLOALT panel WS 60x60
* TRADFRI bulb E27 CWS 806lm
* TRADFRI bulb E27 CWS opal 600lm
* TRADFRI bulb E27 WS globe opal 1055lm
* TRADFRI bulb E27 WS opal 980lm
* TRADFRI bulb E27 WS opal 1000lm
* TRADFRI bulb E27 WS globe opal 1055lm
* TRADFRI bulb T120 E27 WS opal 470lm
* TRADFRI bulb E14 WS opal 400lm
* TRADFRI bulb GU10 WS 400lm
* TRADFRI bulb GU10 WS 345lm
* TRADFRI Driver 10W
* TRADFRI Driver 30W
* GUNNARP panel round
* Light-Controller
* Remote Control N2
* TRADFRI on/off switch
* TRADFRI remote control
* LEPTITER Recessed spot light
* Sound-Controller
* TRADFRI wireless dimmer
* Sound-Controller
* SYMFONISK Sound Controller
* Blinds-Controller
* TRADFRI open/close remote
* Motion-Sensor
* Motion-Sensor
* TRADFRI motion sensor
* Shortcut-Controller
* TRADFRI SHORTCUT Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public Mono<List<_Device>> all() {
.collectList();
}

protected Mono<_Device> refresh(final _Device device) {
public Mono<_Device> refresh(final _Device device) {
return this.clientApi.device.device(device.id)
.flatMap(this::cast);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ protected boolean isInstance(final Device<?, ?> device) {
}

private final ClientApi clientApi;
private final WebSocketApi webSocketApi;
public final WebSocketApi websocket;

public final DeviceApiWrapper device;
public final RoomApi room;
Expand All @@ -116,24 +116,24 @@ protected boolean isInstance(final Device<?, ?> device) {

public DirigeraApi(final ClientApi clientApi) {
this.clientApi = clientApi;
this.webSocketApi = new WebSocketApi(clientApi);
this.websocket = new WebSocketApi(clientApi);
this.device = new DeviceApiWrapper(
clientApi,
this.webSocketApi,
new GatewayDeviceApi(clientApi, this.webSocketApi),
new LightControllerDeviceApi(clientApi, this.webSocketApi),
new LightDeviceApi(clientApi, this.webSocketApi),
new MotionSensorDeviceApi(clientApi, this.webSocketApi),
new OutletDeviceApi(clientApi, this.webSocketApi),
new RepeaterDeviceApi(clientApi, this.webSocketApi),
new ShortcutControllerDeviceApi(clientApi, this.webSocketApi),
new SoundControllerDeviceApi(clientApi, this.webSocketApi),
new AirPurifierDeviceApi(clientApi, this.webSocketApi),
new BlindsControllerDeviceApi(clientApi, this.webSocketApi),
new BlindsDeviceApi(clientApi, this.webSocketApi),
new SpeakerDeviceApi(clientApi, this.webSocketApi));
this.room = new RoomApi(clientApi, this.webSocketApi);
this.scene = new SceneApi(clientApi, this.webSocketApi);
this.websocket,
new GatewayDeviceApi(clientApi, this.websocket),
new LightControllerDeviceApi(clientApi, this.websocket),
new LightDeviceApi(clientApi, this.websocket),
new MotionSensorDeviceApi(clientApi, this.websocket),
new OutletDeviceApi(clientApi, this.websocket),
new RepeaterDeviceApi(clientApi, this.websocket),
new ShortcutControllerDeviceApi(clientApi, this.websocket),
new SoundControllerDeviceApi(clientApi, this.websocket),
new AirPurifierDeviceApi(clientApi, this.websocket),
new BlindsControllerDeviceApi(clientApi, this.websocket),
new BlindsDeviceApi(clientApi, this.websocket),
new SpeakerDeviceApi(clientApi, this.websocket));
this.room = new RoomApi(clientApi, this.websocket);
this.scene = new SceneApi(clientApi, this.websocket);
this.user = new UserApi(clientApi);
}

Expand Down Expand Up @@ -187,10 +187,10 @@ public Mono<GatewayStatus> deactivatePersistentMode() {
}

public <E extends Event> void websocket(Consumer<E> consumer, Class<E> filter) {
this.webSocketApi.addListener(consumer, filter);
this.websocket.addListener(consumer, filter);
}

public void websocket(Consumer<Event> consumer) {
this.webSocketApi.addListener(consumer);
this.websocket.addListener(consumer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,12 @@ public synchronized <E extends Event> void removeListener(final Consumer<E> list
this.listeners.removeIf(l -> l instanceof FilteredEventListener<?> && ((FilteredEventListener<?>) l).listener.equals(listener));
}

void stop() throws InterruptedException {
public void stop() throws InterruptedException {
this.running.set(false);
while (this.thread.isAlive())
this.thread.interrupt();
while (this.thread.isAlive()) {
Thread.sleep(100);
this.thread.interrupt();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public Mono<Void> websocket(final Consumer<Event> consumer) {
.then()
);
} catch (IOException e) {
throw new RuntimeException(e);
return Mono.error(e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.dvdgeisler.iot.dirigera.client.api.model.device;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
Expand All @@ -21,6 +22,7 @@
import java.time.LocalDateTime;
import java.util.List;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "deviceType",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package de.dvdgeisler.iot.dirigera.client.api.model.device;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonUnwrapped;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class DeviceAttributes<_DeviceStateAttributes extends DeviceStateAttributes> {
public String model;
public String manufacturer;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,80 +1,122 @@
package de.dvdgeisler.iot.dirigera.client.mqtt;

import de.dvdgeisler.iot.dirigera.client.api.DirigeraApi;
import de.dvdgeisler.iot.dirigera.client.api.model.device.Device;
import de.dvdgeisler.iot.dirigera.client.api.model.device.light.LightDevice;
import de.dvdgeisler.iot.dirigera.client.api.model.device.motionsensor.MotionSensorDevice;
import de.dvdgeisler.iot.dirigera.client.api.model.device.outlet.OutletDevice;
import de.dvdgeisler.iot.dirigera.client.mqtt.hass.LightEventHandler;
import de.dvdgeisler.iot.dirigera.client.mqtt.hass.MotionSensorEventHandler;
import de.dvdgeisler.iot.dirigera.client.mqtt.hass.OutletEventHandler;
import de.dvdgeisler.iot.dirigera.client.mqtt.hass.HassLightDeviceEventHandler;
import de.dvdgeisler.iot.dirigera.client.mqtt.hass.HassMotionSensorDeviceEventHandler;
import de.dvdgeisler.iot.dirigera.client.mqtt.hass.HassOutletDeviceEventHandler;
import org.eclipse.paho.client.mqttv3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import reactor.core.publisher.Flux;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@SpringBootApplication
@ComponentScan(basePackageClasses = {DirigeraApi.class, MqttBridge.class})
@ComponentScan(basePackageClasses = {
DirigeraApi.class,
HassLightDeviceEventHandler.class,
HassMotionSensorDeviceEventHandler.class,
HassOutletDeviceEventHandler.class})
public class DirigeraClientMqttApplication {
private final static Logger log = LoggerFactory.getLogger(DirigeraClientMqttApplication.class);

final MqttBridge mqttBridge;
final HashMap<String, Device> devices;
private final static int EXIT_SUCCESS = 0;
private final static int EXIT_ERROR = 1;
private final DirigeraApi api;
private final ConfigurableApplicationContext context;

public DirigeraClientMqttApplication(final MqttBridge mqttBridge) {
this.mqttBridge = mqttBridge;
this.devices = new HashMap<>();
public DirigeraClientMqttApplication(
final DirigeraApi api,
final ConfigurableApplicationContext context) {
this.api = api;
this.context = context;
}

@Bean
public CommandLineRunner run(
final DirigeraApi api,
final LightEventHandler lightEventHandler,
final OutletEventHandler outletEventHandler,
final MotionSensorEventHandler motionSensorEventHandler) {
return (String... args) -> {

api.pairIfRequired().block();

this.mqttBridge.registerEventHandler(LightDevice.class, lightEventHandler);
this.mqttBridge.registerEventHandler(OutletDevice.class, outletEventHandler);
this.mqttBridge.registerEventHandler(MotionSensorDevice.class, motionSensorEventHandler);

api.device.all()
.flatMapMany(Flux::fromIterable)
.doOnNext(device -> {
if(!this.devices.containsKey(device.id))
this.mqttBridge.addDevice(device);
})
.doOnNext(this.mqttBridge::updateDevice)
.doOnNext(device -> this.devices.put(device.id, device))
.collectMap(device -> device.id)
.map(devices -> this.devices
.entrySet()
.stream()
.filter(deviceEntry -> !devices.containsKey(deviceEntry.getKey()))
.map(Map.Entry::getValue)
.toList())
.flatMapMany(Flux::fromIterable)
.doOnNext(this.mqttBridge::removeDevice)
.doOnNext(device -> this.devices.remove(device.id))
.collectList()
.delayElement(Duration.ofMillis(100))
.repeat()
.blockLast();
};
public MqttClient getMqttClient(@Value("${dirigera.mqtt.hostname:localhost}") final String host,
@Value("${dirigera.mqtt.port:1883}") final Short port,
@Value("${dirigera.mqtt.username:}") final String username,
@Value("${dirigera.mqtt.password:}") final String password,
@Value("${dirigera.mqtt.reconnect:true}") final Boolean reconnect,
@Value("${dirigera.mqtt.timeout:0}") final Integer timeout,
@Value("${dirigera.mqtt.keep-alive:2}") final Integer keepAliveInterval,
@Value("${dirigera.mqtt.reconnect-delay:1}") final Integer reconnectDelay,
final DirigeraApi api) throws MqttException {
final MqttConnectOptions options;
final MqttClient client;
final String publisherId;
final String uri;

api.pairIfRequired().block();
publisherId = api.status().map(s -> s.id).block();
uri = String.format("tcp://%s:%d", host, port);
client = new MqttClient(uri, publisherId);

log.info("Connect to MQTT broker: host={}, port={}, publisherId={}, reconnect={}, timeout={}",
host, port, publisherId, reconnect, timeout);

options = new MqttConnectOptions();
options.setKeepAliveInterval(keepAliveInterval);
options.setMaxReconnectDelay(reconnectDelay);
options.setAutomaticReconnect(reconnect);
//options.setCleanSession(true);
options.setConnectionTimeout(timeout);

if (!username.isEmpty() && !password.isEmpty()) {
options.setUserName(username);
options.setPassword(password.toCharArray());
}
client.setCallback(new MqttCallback() {
@Override
public void connectionLost(final Throwable cause) {
log.error("Connection lost to MQTT broker: {}", cause.getMessage());
exit(EXIT_ERROR);
}

@Override
public void messageArrived(final String topic, final MqttMessage message) {
}

@Override
public void deliveryComplete(final IMqttDeliveryToken token) {
}
});

try {
client.connect(options);
} catch (MqttException e) {
log.error("Error while connecting to MQTT broker: {}", e.getMessage());
this.exit(EXIT_ERROR);
}

log.info("Connection to MQTT broker successfully established");

return client;
}

public static void main(String[] args) {
SpringApplication.run(DirigeraClientMqttApplication.class, args).close();
SpringApplication.run(DirigeraClientMqttApplication.class, args);
}

public void exit(int status) {

try {
log.info("Close WebSocket");
this.api.websocket.stop();
} catch (InterruptedException e) {
log.error(e.getMessage());
}

if (context != null) {
log.info("Close Spring Boot context");
this.context.close();
}

log.info("Exit application");
Runtime.getRuntime().halt(status);
}

}
Loading

0 comments on commit 2d80173

Please sign in to comment.