Skip to content

Commit

Permalink
Merge pull request #75 from Katalystical/feature/airquality
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdgeisler authored Apr 12, 2023
2 parents bc89124 + 533691d commit 775d29a
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public static class DeviceApiWrapper extends DeviceApi<
public final LightControllerDeviceApi lightController;
public final LightDeviceApi light;
public final MotionSensorDeviceApi motionSensor;
public final EnvironmentSensorDeviceApi environmentSensor;
public final OutletDeviceApi outlet;
public final RepeaterDeviceApi repeater;
public final ShortcutControllerDeviceApi shortcutController;
Expand All @@ -69,6 +70,7 @@ public DeviceApiWrapper(
final LightControllerDeviceApi lightController,
final LightDeviceApi light,
final MotionSensorDeviceApi motionSensor,
final EnvironmentSensorDeviceApi environmentSensor,
final OutletDeviceApi outlet,
final RepeaterDeviceApi repeater,
final ShortcutControllerDeviceApi shortcutController,
Expand All @@ -90,6 +92,7 @@ public DeviceApiWrapper(
this.lightController = lightController;
this.light = light;
this.motionSensor = motionSensor;
this.environmentSensor = environmentSensor;
this.outlet = outlet;
this.repeater = repeater;
this.shortcutController = shortcutController;
Expand Down Expand Up @@ -123,6 +126,7 @@ public DirigeraApi(final ClientApi clientApi) {
new LightControllerDeviceApi(clientApi, this.websocket),
new LightDeviceApi(clientApi, this.websocket),
new MotionSensorDeviceApi(clientApi, this.websocket),
new EnvironmentSensorDeviceApi(clientApi, this.websocket),
new OutletDeviceApi(clientApi, this.websocket),
new RepeaterDeviceApi(clientApi, this.websocket),
new ShortcutControllerDeviceApi(clientApi, this.websocket),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package de.dvdgeisler.iot.dirigera.client.api;

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.environmentsensor.EnvironmentSensorAttributes;
import de.dvdgeisler.iot.dirigera.client.api.model.device.environmentsensor.EnvironmentSensorConfigurationAttributes;
import de.dvdgeisler.iot.dirigera.client.api.model.device.environmentsensor.EnvironmentSensorDevice;
import de.dvdgeisler.iot.dirigera.client.api.model.device.environmentsensor.EnvironmentSensorStateAttributes;

public class EnvironmentSensorDeviceApi extends ControllerDeviceApi<
EnvironmentSensorStateAttributes,
EnvironmentSensorAttributes,
EnvironmentSensorConfigurationAttributes,
EnvironmentSensorDevice> {

public EnvironmentSensorDeviceApi(final ClientApi clientApi, final WebSocketApi webSocketApi) {
super(clientApi, webSocketApi);
}

@Override
protected boolean isInstance(final Device<?, ?> device) {
return super.isInstance(device) && device instanceof EnvironmentSensorDevice;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import de.dvdgeisler.iot.dirigera.client.api.model.device.airpurifier.AirPurifierDevice;
import de.dvdgeisler.iot.dirigera.client.api.model.device.blinds.BlindsDevice;
import de.dvdgeisler.iot.dirigera.client.api.model.device.blindscontroller.BlindsControllerDevice;
import de.dvdgeisler.iot.dirigera.client.api.model.device.environmentsensor.EnvironmentSensorDevice;
import de.dvdgeisler.iot.dirigera.client.api.model.device.gateway.GatewayDevice;
import de.dvdgeisler.iot.dirigera.client.api.model.device.light.LightDevice;
import de.dvdgeisler.iot.dirigera.client.api.model.device.lightcontroller.LightControllerDevice;
Expand All @@ -34,6 +35,7 @@
@JsonSubTypes.Type(value = LightControllerDevice.class, name = "lightController"),
@JsonSubTypes.Type(value = SoundControllerDevice.class, name = "soundController"),
@JsonSubTypes.Type(value = MotionSensorDevice.class, name = "motionSensor"),
@JsonSubTypes.Type(value = EnvironmentSensorDevice.class, name = "environmentSensor"),
@JsonSubTypes.Type(value = OutletDevice.class, name = "outlet"),
@JsonSubTypes.Type(value = ShortcutControllerDevice.class, name = "shortcutController"),
@JsonSubTypes.Type(value = RepeaterDevice.class, name = "repeater"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package de.dvdgeisler.iot.dirigera.client.api.model.device.environmentsensor;

import com.fasterxml.jackson.annotation.JsonUnwrapped;
import de.dvdgeisler.iot.dirigera.client.api.model.device.DeviceAttributes;
import de.dvdgeisler.iot.dirigera.client.api.model.device.ota.OtaAttributes;

public class EnvironmentSensorAttributes extends DeviceAttributes<EnvironmentSensorStateAttributes> {
public String productCode;
public Boolean permittingJoin;
public Integer currentTemperature;
public Integer currentRH;
public Integer currentPM25;
public Integer maxMeasuredPM25;
public Integer minMeasuredPM25;
public Integer vocIndex;

@JsonUnwrapped
public OtaAttributes ota;

public EnvironmentSensorAttributes() {
}

public EnvironmentSensorAttributes(
final String model,
final String manufacturer,
final String firmwareVersion,
final String hardwareVersion,
final String serialNumber,
final String productCode,
final Boolean permittingJoin,
final EnvironmentSensorStateAttributes state,
final OtaAttributes ota) {
super(model, manufacturer, firmwareVersion, hardwareVersion, serialNumber, state);
this.productCode = productCode;
this.permittingJoin = permittingJoin;
this.ota = ota;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package de.dvdgeisler.iot.dirigera.client.api.model.device.environmentsensor;

import de.dvdgeisler.iot.dirigera.client.api.model.device.DeviceConfigurationDefaultAttributes;
import de.dvdgeisler.iot.dirigera.client.api.model.deviceset.DeviceSet;
import de.dvdgeisler.iot.dirigera.client.api.model.deviceset.Room;

import java.util.List;

public class EnvironmentSensorConfigurationAttributes extends DeviceConfigurationDefaultAttributes {

public EnvironmentSensorConfigurationAttributes(final String customIcon, final List<DeviceSet> deviceSet, final Boolean isHidden, final Room room) {
super(customIcon, deviceSet, isHidden, room);
}

public EnvironmentSensorConfigurationAttributes() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package de.dvdgeisler.iot.dirigera.client.api.model.device.environmentsensor;

import de.dvdgeisler.iot.dirigera.client.api.model.device.Device;
import de.dvdgeisler.iot.dirigera.client.api.model.device.DeviceCapabilities;

import java.time.LocalDateTime;
import java.util.List;

import static de.dvdgeisler.iot.dirigera.client.api.model.device.DeviceCategory.SENSOR;
import static de.dvdgeisler.iot.dirigera.client.api.model.device.DeviceType.ENVIRONMENT_SENSOR;

public class EnvironmentSensorDevice extends Device<EnvironmentSensorAttributes, EnvironmentSensorConfigurationAttributes> {

public EnvironmentSensorDevice() {
}

public EnvironmentSensorDevice(
final String id,
final LocalDateTime createdAt,
final Boolean isReachable,
final LocalDateTime lastSeen,
final EnvironmentSensorAttributes attributes,
final DeviceCapabilities capabilities,
final List<String> remoteLinks,
final EnvironmentSensorConfigurationAttributes environmentSensorConfigurationAttributes
) {
super(id, SENSOR, ENVIRONMENT_SENSOR, createdAt, isReachable, lastSeen, attributes, capabilities, remoteLinks, environmentSensorConfigurationAttributes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package de.dvdgeisler.iot.dirigera.client.api.model.device.environmentsensor;

import de.dvdgeisler.iot.dirigera.client.api.model.device.DeviceStateAttributes;

public class EnvironmentSensorStateAttributes extends DeviceStateAttributes {

public EnvironmentSensorStateAttributes(final String customName) {
super(customName);
}

public EnvironmentSensorStateAttributes() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package de.dvdgeisler.iot.dirigera.client.api.model.device.environmentsensor;

import de.dvdgeisler.iot.dirigera.client.api.model.device.Device;
import de.dvdgeisler.iot.dirigera.client.api.model.device.DeviceTest;

import static org.junit.jupiter.api.Assertions.assertTrue;

class VINDSTYRKAAirQualitySensor extends DeviceTest {
final static String JSON = """
{
"id" : "XXXXXXXXXXXXXXXXXXX",
"type" : "sensor",
"deviceType" : "environmentSensor",
"createdAt" : "2023-03-10T13:23:42.000Z",
"isReachable" : true,
"lastSeen" : "2023-03-26T14:10:16.000Z",
"attributes" : {
"customName" : "Luftsensor",
"firmwareVersion" : "1.0.11",
"hardwareVersion" : "1",
"manufacturer" : "IKEA of Sweden",
"model" : "VINDSTYRKA",
"productCode" : "E2112",
"serialNumber" : "XXXXXXXXXXXX",
"currentTemperature" : 19,
"currentRH" : 52,
"currentPM25" : 2,
"maxMeasuredPM25" : 999,
"minMeasuredPM25" : 0,
"vocIndex" : 112,
"identifyPeriod" : 0,
"identifyStarted" : "2000-01-01T00:00:00.000Z",
"permittingJoin" : false,
"otaPolicy" : "autoUpdate",
"otaProgress" : 0,
"otaScheduleEnd" : "00:00",
"otaScheduleStart" : "00:00",
"otaState" : "readyToCheck",
"otaStatus" : "upToDate"
},
"capabilities" : {
"canSend" : [ ],
"canReceive" : [ "customName" ]
},
"room" : {
"id" : "255c7093-f8bc-4ea6-aa32-e352f673b716",
"name" : "Schlafzimmer ",
"color" : "ikea_yellow_no_24",
"icon" : "rooms_bed"
},
"deviceSet" : [ ],
"remoteLinks" : [ ],
"isHidden" : false
}
""";

public VINDSTYRKAAirQualitySensor() {
super(JSON);
}

@Override
public void validateDeserialize(final Device<?,?> device) {
assertTrue(device instanceof EnvironmentSensorDevice);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package de.dvdgeisler.iot.dirigera.client.examples.airquality;

import de.dvdgeisler.iot.dirigera.client.api.DirigeraApi;
import de.dvdgeisler.iot.dirigera.client.api.model.device.DeviceType;
import de.dvdgeisler.iot.dirigera.client.api.model.device.environmentsensor.EnvironmentSensorDevice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import reactor.core.publisher.Flux;

/**
* Monitor air quality
*/
@SpringBootApplication
@ComponentScan(basePackageClasses = {DirigeraApi.class})
@EnableScheduling
public class AirQualityMonitor {
private final static Logger log = LoggerFactory.getLogger(AirQualityMonitor.class);

private final DirigeraApi api;

public AirQualityMonitor(final DirigeraApi api) {
this.api = api;
}

@Scheduled(fixedRate = 1000)
public void monitor() {
this.api.device.environmentSensor.all()
.flatMapMany(Flux::fromIterable)
.filter(d -> d.deviceType == DeviceType.ENVIRONMENT_SENSOR)
.cast(EnvironmentSensorDevice.class)
.doOnNext(this::printAirQualityData)
.blockLast();
}

private void printAirQualityData(final EnvironmentSensorDevice sensor) {
log.info("{}: Temperature={}, RelativeHumidity={}, PM25={}, VOC={}, isReachable={}",
sensor.attributes.state.customName != null && !sensor.attributes.state.customName.isBlank() ? sensor.attributes.state.customName : sensor.id,
sensor.attributes.currentTemperature,
sensor.attributes.currentRH,
sensor.attributes.currentPM25,
sensor.attributes.vocIndex,
sensor.isReachable);
}

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


}

0 comments on commit 775d29a

Please sign in to comment.