[electroluxair] Updated to changed API (#15265)

Signed-off-by: Jan Gustafsson <jannegpriv@gmail.com>
This commit is contained in:
Jan Gustafsson 2023-07-22 16:26:03 +02:00 committed by GitHub
parent 2129300fd2
commit 2982cc8911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 353 additions and 527 deletions

View File

@ -2,8 +2,6 @@
This is an openHAB binding for the Pure A9 Air Purifier, by Electrolux.
This binding uses the Electrolux Delta REST API.
![Electrolux Pure A9](doc/electrolux_pure_a9.png)
## Supported Things
@ -26,15 +24,15 @@ Only the bridge require manual configuration. The Electrolux Pure A9 thing can b
| Parameter | Description | Type | Default | Required |
|-----------|--------------------------------------------------------------|--------|----------|----------|
| username | The username used to connect to the Electrolux Wellbeing app | String | NA | yes |
| password | The password used to connect to the Electrolux Wellbeing app | String | NA | yes |
| username | The username used to connect to the Electrolux app | String | NA | yes |
| password | The password used to connect to the Electrolux app | String | NA | yes |
| refresh | Specifies the refresh interval in second | Number | 600 | yes |
#### Electrolux Pure A9
| Parameter | Description | Type | Default | Required |
|-----------|-------------------------------------------------------------------------|--------|----------|----------|
| deviceId | Product ID of your Electrolux Pure A9 found in Electrolux Wellbeing app | Number | NA | yes |
| deviceId | Product ID of your Electrolux Pure A9 found in Electrolux app | Number | NA | yes |
## Channels
@ -53,9 +51,10 @@ The following channels are supported:
| co2 | Number:Dimensionless | This channel reports the CO2 level in ppm. |
| fanSpeed | Number | This channel sets and reports the current fan speed (1-9). |
| filterLife | Number:Dimensionless | This channel reports the remaining filter life in %. |
| ionizer | Switch | This channel sets and reports the status of the ionizer function (On/Off). |
| ionizer | Switch | This channel sets and reports the status of the Ionizer function (On/Off). |
| doorOpen | Contact | This channel reports the status of door (Opened/Closed). |
| workMode | String | This channel sets and reports the current work mode (Auto, Manual, PowerOff.)|
| workMode | String | This channel sets and reports the current work mode (Auto, Manual, PowerOff). |
## Full Example
@ -83,4 +82,7 @@ Contact ElectroluxAirDoor "Electrolux Air Door Status" {channel="electroluxair:e
String ElectroluxAirWorkModeSetting "ElectroluxAir Work Mode Setting" {channel="electroluxair:electroluxpurea9:myAPI:myElectroluxPureA9:workMode"}
// Fan speed
Number ElectroluxAirFanSpeed "Electrolux Air Fan Speed Setting" {channel="electroluxair:electroluxpurea9:myAPI:myElectroluxPureA9:fanSpeed"}
// Ionizer
Switch ElectroluxAirIonizer "Electrolux Air Ionizer Setting" {channel="electroluxair:electroluxpurea9:myAPI:myElectroluxPureA9:ionizer"}
```

View File

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

View File

@ -12,6 +12,7 @@
*/
package org.openhab.binding.electroluxair.internal.api;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@ -27,17 +28,13 @@ import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.electroluxair.internal.ElectroluxAirBridgeConfiguration;
import org.openhab.binding.electroluxair.internal.ElectroluxAirException;
import org.openhab.binding.electroluxair.internal.dto.ElectroluxPureA9DTO;
import org.openhab.binding.electroluxair.internal.dto.ElectroluxPureA9DTO.AppliancesInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
/**
* The {@link ElectroluxDeltaAPI} class defines the Elextrolux Delta API
@ -46,10 +43,17 @@ import com.google.gson.annotations.SerializedName;
*/
@NonNullByDefault
public class ElectroluxDeltaAPI {
private static final String CLIENT_URL = "https://electrolux-wellbeing-client.vercel.app/api/mu52m5PR9X";
private static final String SERVICE_URL = "https://api.delta.electrolux.com/api/";
private static final String CLIENT_ID = "ElxOneApp";
private static final String CLIENT_SECRET = "8UKrsKD7jH9zvTV7rz5HeCLkit67Mmj68FvRVTlYygwJYy4dW6KF2cVLPKeWzUQUd6KJMtTifFf4NkDnjI7ZLdfnwcPtTSNtYvbP7OzEkmQD9IjhMOf5e1zeAQYtt2yN";
private static final String X_API_KEY = "2AMqwEV5MqVhTKrRCyYfVF8gmKrd2rAmp7cUsfky";
private static final String BASE_URL = "https://api.ocp.electrolux.one";
private static final String TOKEN_URL = BASE_URL + "/one-account-authorization/api/v1/token";
private static final String AUTHENTICATION_URL = BASE_URL + "/one-account-authentication/api/v1/authenticate";
private static final String API_URL = BASE_URL + "/appliance/api/v2";
private static final String APPLIANCES_URL = API_URL + "/appliances";
private static final String JSON_CONTENT_TYPE = "application/json";
private static final String LOGIN = "Users/Login";
private static final int MAX_RETRIES = 3;
private final Logger logger = LoggerFactory.getLogger(ElectroluxDeltaAPI.class);
@ -57,6 +61,7 @@ public class ElectroluxDeltaAPI {
private final HttpClient httpClient;
private final ElectroluxAirBridgeConfiguration configuration;
private String authToken = "";
private Instant tokenExpiry = Instant.MIN;
public ElectroluxDeltaAPI(ElectroluxAirBridgeConfiguration configuration, Gson gson, HttpClient httpClient) {
this.gson = gson;
@ -66,70 +71,73 @@ public class ElectroluxDeltaAPI {
public boolean refresh(Map<String, ElectroluxPureA9DTO> electroluxAirThings) {
try {
// Login
login();
if (Instant.now().isAfter(this.tokenExpiry)) {
// Login again since token is expired
login();
}
// Get all appliances
String json = getAppliances();
JsonArray jsonArray = JsonParser.parseString(json).getAsJsonArray();
for (JsonElement jsonElement : jsonArray) {
String pncId = jsonElement.getAsJsonObject().get("pncId").getAsString();
// Get appliance info
String jsonApplianceInfo = getAppliancesInfo(pncId);
AppliancesInfo appliancesInfo = gson.fromJson(jsonApplianceInfo, AppliancesInfo.class);
// Get applicance data
ElectroluxPureA9DTO dto = getAppliancesData(pncId, ElectroluxPureA9DTO.class);
if (appliancesInfo != null) {
dto.setApplicancesInfo(appliancesInfo);
ElectroluxPureA9DTO[] dtos = gson.fromJson(json, ElectroluxPureA9DTO[].class);
if (dtos != null) {
for (ElectroluxPureA9DTO dto : dtos) {
String applianceId = dto.getApplianceId();
// Get appliance info
String jsonApplianceInfo = getAppliancesInfo(applianceId);
ElectroluxPureA9DTO.ApplianceInfo applianceInfo = gson.fromJson(jsonApplianceInfo,
ElectroluxPureA9DTO.ApplianceInfo.class);
if (applianceInfo != null) {
if ("AIR_PURIFIER".equals(applianceInfo.getDeviceType())) {
dto.setApplianceInfo(applianceInfo);
electroluxAirThings.put(dto.getProperties().getReported().getDeviceId(), dto);
}
}
}
electroluxAirThings.put(dto.getTwin().getProperties().getReported().deviceId, dto);
return true;
}
return true;
} catch (ElectroluxAirException e) {
} catch (JsonSyntaxException | ElectroluxAirException e) {
logger.warn("Failed to refresh! {}", e.getMessage());
}
return false;
}
public boolean workModePowerOff(String pncId) {
public boolean workModePowerOff(String applianceId) {
String commandJSON = "{ \"WorkMode\": \"PowerOff\" }";
try {
return sendCommand(commandJSON, pncId);
return sendCommand(commandJSON, applianceId);
} catch (ElectroluxAirException e) {
logger.warn("Work mode powerOff failed {}", e.getMessage());
}
return false;
}
public boolean workModeAuto(String pncId) {
public boolean workModeAuto(String applianceId) {
String commandJSON = "{ \"WorkMode\": \"Auto\" }";
try {
return sendCommand(commandJSON, pncId);
return sendCommand(commandJSON, applianceId);
} catch (ElectroluxAirException e) {
logger.warn("Work mode auto failed {}", e.getMessage());
}
return false;
}
public boolean workModeManual(String pncId) {
public boolean workModeManual(String applianceId) {
String commandJSON = "{ \"WorkMode\": \"Manual\" }";
try {
return sendCommand(commandJSON, pncId);
return sendCommand(commandJSON, applianceId);
} catch (ElectroluxAirException e) {
logger.warn("Work mode manual failed {}", e.getMessage());
}
return false;
}
public boolean setFanSpeedLevel(String pncId, int fanSpeedLevel) {
public boolean setFanSpeedLevel(String applianceId, int fanSpeedLevel) {
if (fanSpeedLevel < 1 && fanSpeedLevel > 10) {
return false;
} else {
String commandJSON = "{ \"Fanspeed\": " + fanSpeedLevel + "}";
try {
return sendCommand(commandJSON, pncId);
return sendCommand(commandJSON, applianceId);
} catch (ElectroluxAirException e) {
logger.warn("Work mode manual failed {}", e.getMessage());
}
@ -137,40 +145,54 @@ public class ElectroluxDeltaAPI {
return false;
}
public boolean setIonizer(String pncId, String ionizerStatus) {
public boolean setIonizer(String applianceId, String ionizerStatus) {
String commandJSON = "{ \"Ionizer\": " + ionizerStatus + "}";
try {
return sendCommand(commandJSON, pncId);
return sendCommand(commandJSON, applianceId);
} catch (ElectroluxAirException e) {
logger.warn("Work mode manual failed {}", e.getMessage());
}
return false;
}
private void login() throws ElectroluxAirException {
// Fetch ClientToken
Request request = httpClient.newRequest(CLIENT_URL).method(HttpMethod.GET);
private Request createRequest(String uri, HttpMethod httpMethod) {
Request request = httpClient.newRequest(uri).method(httpMethod);
request.header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE);
request.header(HttpHeader.CONTENT_TYPE, JSON_CONTENT_TYPE);
logger.debug("HTTP GET Request {}.", request.toString());
logger.debug("HTTP POST Request {}.", request.toString());
return request;
}
private void login() throws ElectroluxAirException {
try {
String json = "{\"clientId\": \"" + CLIENT_ID + "\", \"clientSecret\": \"" + CLIENT_SECRET
+ "\", \"grantType\": \"client_credentials\"}";
// Fetch ClientToken
Request request = createRequest(TOKEN_URL, HttpMethod.POST);
request.content(new StringContentProvider(json), JSON_CONTENT_TYPE);
logger.debug("HTTP POST Request {}.", request.toString());
ContentResponse httpResponse = request.send();
if (httpResponse.getStatus() != HttpStatus.OK_200) {
throw new ElectroluxAirException("Failed to login " + httpResponse.getContentAsString());
throw new ElectroluxAirException("Failed to get token 1" + httpResponse.getContentAsString());
}
String json = httpResponse.getContentAsString();
json = httpResponse.getContentAsString();
logger.trace("Token 1: {}", json);
JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
String clientToken = jsonObject.get("accessToken").getAsString();
// Login using ClientToken
json = "{ \"Username\": \"" + configuration.username + "\", \"Password\": \"" + configuration.password
// Login using access token 1
json = "{ \"username\": \"" + configuration.username + "\", \"password\": \"" + configuration.password
+ "\" }";
request = httpClient.newRequest(SERVICE_URL + LOGIN).method(HttpMethod.POST);
request.header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE);
request.header(HttpHeader.CONTENT_TYPE, JSON_CONTENT_TYPE);
request = createRequest(AUTHENTICATION_URL, HttpMethod.POST);
request.header(HttpHeader.AUTHORIZATION, "Bearer " + clientToken);
request.header("x-api-key", X_API_KEY);
request.content(new StringContentProvider(json), JSON_CONTENT_TYPE);
logger.debug("HTTP POST Request {}.", request.toString());
@ -179,10 +201,33 @@ public class ElectroluxDeltaAPI {
if (httpResponse.getStatus() != HttpStatus.OK_200) {
throw new ElectroluxAirException("Failed to login " + httpResponse.getContentAsString());
}
json = httpResponse.getContentAsString();
logger.trace("Token 2: {}", json);
jsonObject = JsonParser.parseString(json).getAsJsonObject();
String idToken = jsonObject.get("idToken").getAsString();
String countryCode = jsonObject.get("countryCode").getAsString();
String credentials = "{\"clientId\": \"" + CLIENT_ID + "\", \"idToken\": \"" + idToken
+ "\", \"grantType\": \"urn:ietf:params:oauth:grant-type:token-exchange\"}";
// Fetch access token 2
request = createRequest(TOKEN_URL, HttpMethod.POST);
request.header("Origin-Country-Code", countryCode);
request.content(new StringContentProvider(credentials), JSON_CONTENT_TYPE);
logger.debug("HTTP POST Request {}.", request.toString());
httpResponse = request.send();
if (httpResponse.getStatus() != HttpStatus.OK_200) {
throw new ElectroluxAirException("Failed to get token 1" + httpResponse.getContentAsString());
}
// Fetch AccessToken
json = httpResponse.getContentAsString();
logger.trace("AccessToken: {}", json);
jsonObject = JsonParser.parseString(json).getAsJsonObject();
this.authToken = jsonObject.get("accessToken").getAsString();
int expiresIn = jsonObject.get("expiresIn").getAsInt();
this.tokenExpiry = Instant.now().plusSeconds(expiresIn);
} catch (InterruptedException | TimeoutException | ExecutionException e) {
throw new ElectroluxAirException(e);
}
@ -192,10 +237,9 @@ public class ElectroluxDeltaAPI {
try {
for (int i = 0; i < MAX_RETRIES; i++) {
try {
Request request = httpClient.newRequest(SERVICE_URL + uri).method(HttpMethod.GET);
Request request = createRequest(uri, HttpMethod.GET);
request.header(HttpHeader.AUTHORIZATION, "Bearer " + authToken);
request.header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE);
request.header(HttpHeader.CONTENT_TYPE, JSON_CONTENT_TYPE);
request.header("x-api-key", X_API_KEY);
ContentResponse response = request.send();
String content = response.getContentAsString();
@ -218,44 +262,28 @@ public class ElectroluxDeltaAPI {
}
private String getAppliances() throws ElectroluxAirException {
String uri = "Domains/Appliances";
try {
return getFromApi(uri);
return getFromApi(APPLIANCES_URL);
} catch (ElectroluxAirException | InterruptedException e) {
throw new ElectroluxAirException(e);
}
}
private String getAppliancesInfo(String pncId) throws ElectroluxAirException {
String uri = "AppliancesInfo/" + pncId;
private String getAppliancesInfo(String applianceId) throws ElectroluxAirException {
try {
return getFromApi(uri);
return getFromApi(APPLIANCES_URL + "/" + applianceId + "/info");
} catch (ElectroluxAirException | InterruptedException e) {
throw new ElectroluxAirException(e);
}
}
private <T> T getAppliancesData(String pncId, Class<T> dto) throws ElectroluxAirException {
String uri = "Appliances/" + pncId;
String json;
try {
json = getFromApi(uri);
} catch (ElectroluxAirException | InterruptedException e) {
throw new ElectroluxAirException(e);
}
return gson.fromJson(json, dto);
}
private boolean sendCommand(String commandJSON, String pncId) throws ElectroluxAirException {
String uri = "Appliances/" + pncId + "/Commands";
private boolean sendCommand(String commandJSON, String applianceId) throws ElectroluxAirException {
try {
for (int i = 0; i < MAX_RETRIES; i++) {
try {
Request request = httpClient.newRequest(SERVICE_URL + uri).method(HttpMethod.PUT);
Request request = createRequest(APPLIANCES_URL + "/" + applianceId + "/command", HttpMethod.PUT);
request.header(HttpHeader.AUTHORIZATION, "Bearer " + authToken);
request.header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE);
request.header(HttpHeader.CONTENT_TYPE, JSON_CONTENT_TYPE);
request.header("x-api-key", X_API_KEY);
request.content(new StringContentProvider(commandJSON), JSON_CONTENT_TYPE);
ContentResponse response = request.send();
@ -266,19 +294,7 @@ public class ElectroluxDeltaAPI {
logger.debug("sendCommand failed, HTTP status: {}", response.getStatus());
login();
} else {
CommandResponseDTO commandResponse = gson.fromJson(content, CommandResponseDTO.class);
if (commandResponse != null) {
if (commandResponse.code == 200000) {
return true;
} else {
logger.warn("Failed to send command, error code: {}, description: {}",
commandResponse.code, commandResponse.codeDescription);
return false;
}
} else {
logger.warn("Failed to send command, commandResponse is null!");
return false;
}
return true;
}
} catch (TimeoutException | InterruptedException e) {
logger.warn("TimeoutException error in get");
@ -289,26 +305,4 @@ public class ElectroluxDeltaAPI {
}
return false;
}
@SuppressWarnings("unused")
private static class CommandResponseDTO {
public int code;
public String codeDescription = "";
public String information = "";
public String message = "";
public PayloadDTO payload = new PayloadDTO();
public int status;
}
private static class PayloadDTO {
@SerializedName("Ok")
public boolean ok;
@SerializedName("Response")
public ResponseDTO response = new ResponseDTO();
}
private static class ResponseDTO {
@SerializedName("Workmode")
public String workmode = "";
}
}

View File

@ -12,6 +12,9 @@
*/
package org.openhab.binding.electroluxair.internal.dto;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.SerializedName;
@ -23,354 +26,232 @@ import com.google.gson.annotations.SerializedName;
*/
@NonNullByDefault
public class ElectroluxPureA9DTO {
public String pncId = "";
public ApplianceData applianceData = new ApplianceData();
public AppliancesInfo applicancesInfo = new AppliancesInfo();
private String applianceId = "";
private ApplianceInfo applianceInfo = new ApplianceInfo();
private ApplianceData applianceData = new ApplianceData();
private Properties properties = new Properties();
private String status = "";
private String connectionState = "";
public Twin twin = new Twin();
public String telemetry = "";
public String getApplianceId() {
return applianceId;
}
public String getPncId() {
return pncId;
public ApplianceInfo getApplianceInfo() {
return applianceInfo;
}
public void setApplianceInfo(ApplianceInfo applianceInfo) {
this.applianceInfo = applianceInfo;
}
public ApplianceData getApplianceData() {
return applianceData;
}
public AppliancesInfo getApplicancesInfo() {
return applicancesInfo;
public Properties getProperties() {
return properties;
}
public void setApplicancesInfo(AppliancesInfo applicancesInfo) {
this.applicancesInfo = applicancesInfo;
public String getStatus() {
return status;
}
public Twin getTwin() {
return twin;
public String getConnectionState() {
return connectionState;
}
public String getTelemetry() {
return telemetry;
public class ApplianceInfo {
private String manufacturingDateCode = "";
private String serialNumber = "";
private String pnc = "";
private String brand = "";
private String market = "";
private String productArea = "";
private String deviceType = "";
private String project = "";
private String model = "";
private String variant = "";
private String colour = "";
public String getManufacturingDateCode() {
return manufacturingDateCode;
}
public String getSerialNumber() {
return serialNumber;
}
public String getPnc() {
return pnc;
}
public String getBrand() {
return brand;
}
public String getMarket() {
return market;
}
public String getProductArea() {
return productArea;
}
public String getDeviceType() {
return deviceType;
}
public String getProject() {
return project;
}
public String getModel() {
return model;
}
public String getVariant() {
return variant;
}
public String getColour() {
return colour;
}
}
public class MetaData1 {
class ApplianceData {
private String applianceName = "";
private String created = "";
private String modelName = "";
@SerializedName("$lastUpdated")
public String lastUpdated1 = "";
@SerializedName("$lastUpdatedVersion")
public int lastUpdatedVersion1;
@SerializedName("TimeZoneStandardName")
public TimeZoneStandardName timeZoneStandardName = new TimeZoneStandardName();
@SerializedName("FrmVer_NIU")
public FrmVerNIU frmVerNIU = new FrmVerNIU();
}
public String getApplianceName() {
return applianceName;
}
public class Metadata2 {
public String getCreated() {
return created;
}
@SerializedName("$lastUpdated")
public String lastUpdated2 = "";
@SerializedName("FrmVer_NIU")
public FrmVerNIU frmVerNIU = new FrmVerNIU();
@SerializedName("Workmode")
public Workmode workmode = new Workmode();
@SerializedName("FilterRFID")
public FilterRFID filterRFID = new FilterRFID();
@SerializedName("FilterLife")
public FilterLife filterLife = new FilterLife();
@SerializedName("Fanspeed")
public Fanspeed fanspeed = new Fanspeed();
@SerializedName("UILight")
public UILight uILight = new UILight();
@SerializedName("SafetyLock")
public SafetyLock safetyLock = new SafetyLock();
@SerializedName("Ionizer")
public Ionizer ionizer = new Ionizer();
@SerializedName("Sleep")
public Sleep sleep = new Sleep();
@SerializedName("Scheduler")
public Scheduler scheduler = new Scheduler();
@SerializedName("FilterType")
public FilterType filterType = new FilterType();
@SerializedName("DspIcoPM2_5")
public DspIcoPM25 dspIcoPM25 = new DspIcoPM25();
@SerializedName("DspIcoPM1")
public DspIcoPM1 dspIcoPM1 = new DspIcoPM1();
@SerializedName("DspIcoPM10")
public DspIcoPM10 dspIcoPM10 = new DspIcoPM10();
@SerializedName("DspIcoTVOC")
public DspIcoTVOC dspIcoTVOC = new DspIcoTVOC();
@SerializedName("ErrPM2_5")
public ErrPM25 errPM25 = new ErrPM25();
@SerializedName("ErrTVOC")
public ErrTVOC errTVOC = new ErrTVOC();
@SerializedName("ErrTempHumidity")
public ErrTempHumidity errTempHumidity = new ErrTempHumidity();
@SerializedName("ErrFanMtr")
public ErrFanMtr errFanMtr = new ErrFanMtr();
@SerializedName("ErrCommSensorDisplayBrd")
public ErrCommSensorDisplayBrd errCommSensorDisplayBrd = new ErrCommSensorDisplayBrd();
@SerializedName("DoorOpen")
public DoorOpen doorOpen = new DoorOpen();
@SerializedName("ErrRFID")
public ErrRFID errRFID = new ErrRFID();
@SerializedName("SignalStrength")
public SignalStrength signalStrength = new SignalStrength();
@SerializedName("PM1")
public PM1 pM1 = new PM1();
@SerializedName("PM2_5")
public PM25 pM25 = new PM25();
@SerializedName("PM10")
public PM10 pM10 = new PM10();
@SerializedName("TVOC")
public TVOC tVOC = new TVOC();
@SerializedName("CO2")
public CO2 cO2 = new CO2();
@SerializedName("Temp")
public Temp temp = new Temp();
@SerializedName("Humidity")
public Humidity humidity = new Humidity();
@SerializedName("EnvLightLvl")
public EnvLightLvl envLightLvl = new EnvLightLvl();
@SerializedName("RSSI")
public RSSI rSSI = new RSSI();
}
public class ApplianceData {
public String applianceName = "";
public String created = "";
public String modelName = "";
public String pncId = "";
}
public class AppliancesInfo {
public String brand = "";
public String colour = "";
public String device = "";
public String model = "";
public String serialNumber = "";
}
public class CO2 {
@SerializedName("$lastUpdated")
public String lastUpdated3 = "";
}
public class Desired {
@SerializedName("TimeZoneStandardName")
public String timeZoneStandardName = "";
@SerializedName("FrmVer_NIU")
public String frmVerNIU = "";
@SerializedName("$metadata")
public MetaData1 metadata3 = new MetaData1();
@SerializedName("$version")
public int version;
}
public class DoorOpen {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class DspIcoPM1 {
@SerializedName("lastUpdated")
public String lastUpdated = "";
}
public class DspIcoPM10 {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class DspIcoPM25 {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class DspIcoTVOC {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class EnvLightLvl {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class ErrCommSensorDisplayBrd {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class ErrFanMtr {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class ErrPM25 {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class ErrRFID {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class ErrTVOC {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class ErrTempHumidity {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class Fanspeed {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class FilterLife {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class FilterRFID {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class FilterType {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class FrmVerNIU {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
@SerializedName("$lastUpdatedVersion")
public int lastUpdatedVersion;
}
// public class FrmVerNIU_ {
// @SerializedName("$lastUpdated")
// public String lastUpdated = "";
// }
public class Humidity {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class Ionizer {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class PM1 {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class PM10 {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class PM25 {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
public String getModelName() {
return modelName;
}
}
public class Properties {
public Desired desired = new Desired();
public Reported reported = new Reported();
private Desired desired = new Desired();
private Reported reported = new Reported();
private Object metadata = new Object();
public Desired getDesired() {
return desired;
}
public Reported getReported() {
return reported;
}
public Object getMetadata() {
return metadata;
}
}
public class RSSI {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
class Desired {
@SerializedName("TimeZoneStandardName")
private String timeZoneStandardName = "";
@SerializedName("FrmVer_NIU")
private String frmVerNIU = "";
@SerializedName("LocationReq")
private boolean locationReq;
private Map<String, Object> metadata = new HashMap<>();
private int version;
public String getTimeZoneStandardName() {
return timeZoneStandardName;
}
public String getFrmVerNIU() {
return frmVerNIU;
}
public boolean isLocationReq() {
return locationReq;
}
public Map<String, Object> getMetadata() {
return metadata;
}
public int getVersion() {
return version;
}
}
public class Reported {
@SerializedName("FrmVer_NIU")
public String frmVerNIU = "";
private String frmVerNIU = "";
@SerializedName("Workmode")
public String workmode = "";
private String workmode = "";
@SerializedName("FilterRFID")
public String filterRFID = "";
private String filterRFID = "";
@SerializedName("FilterLife")
public int filterLife;
private int filterLife = 0;
@SerializedName("Fanspeed")
public int fanspeed;
private int fanSpeed = 0;
@SerializedName("UILight")
public boolean uILight;
private boolean uiLight = false;
@SerializedName("SafetyLock")
public boolean safetyLock;
private boolean safetyLock = false;
@SerializedName("Ionizer")
public boolean ionizer;
private boolean ionizer = false;
@SerializedName("Sleep")
public boolean sleep;
private boolean sleep = false;
@SerializedName("Scheduler")
public boolean scheduler;
private boolean scheduler = false;
@SerializedName("FilterType")
public int filterType;
private int filterType = 0;
@SerializedName("DspIcoPM2_5")
public boolean dspIcoPM25;
private boolean dspIcoPM25 = false;
@SerializedName("DspIcoPM1")
public boolean dspIcoPM1;
private boolean dspIcoPM1 = false;
@SerializedName("DspIcoPM10")
public boolean dspIcoPM10;
private boolean dspIcoPM10 = false;
@SerializedName("DspIcoTVOC")
public boolean dspIcoTVOC;
private boolean dspIcoTVOC = false;
@SerializedName("ErrPM2_5")
public boolean errPM25;
private boolean errPM25 = false;
@SerializedName("ErrTVOC")
public boolean errTVOC;
private boolean errTVOC = false;
@SerializedName("ErrTempHumidity")
public boolean errTempHumidity;
private boolean errTempHumidity = false;
@SerializedName("ErrFanMtr")
public boolean errFanMtr;
private boolean errFanMtr = false;
@SerializedName("ErrCommSensorDisplayBrd")
public boolean errCommSensorDisplayBrd;
private boolean errCommSensorDisplayBrd = false;
@SerializedName("DoorOpen")
public boolean doorOpen;
private boolean doorOpen = false;
@SerializedName("ErrRFID")
public boolean errRFID;
private boolean errRFID = false;
@SerializedName("SignalStrength")
public String signalStrength = "";
@SerializedName("$metadata")
public Metadata2 metadata2 = new Metadata2();
@SerializedName("$version")
public int version;
public String deviceId = "";
@SerializedName("PM1")
public int pM1;
@SerializedName("PM2_5")
public int pM25;
@SerializedName("PM10")
public int pM10;
@SerializedName("TVOC")
public int tVOC;
private String signalStrength = "";
private Map<String, Object> metadata = new HashMap<>();
private int version = 0;
private String deviceId = "";
@SerializedName("CO2")
public int cO2;
private int co2 = 0;
@SerializedName("TVOC")
private int tvoc = 0;
@SerializedName("Temp")
public int temp;
private int temp = 0;
@SerializedName("Humidity")
public int humidity;
@SerializedName("EnvLightLvl")
public int envLightLvl;
private int humidity = 0;
@SerializedName("RSSI")
public int rSSI;
private int rssi = 0;
@SerializedName("PM1")
private int pm1 = 0;
@SerializedName("PM2_5")
private int pm25 = 0;
@SerializedName("PM10")
private int pm10 = 0;
public String getFrmVerNIU() {
return frmVerNIU;
@ -389,11 +270,11 @@ public class ElectroluxPureA9DTO {
}
public int getFanspeed() {
return fanspeed;
return fanSpeed;
}
public boolean isuILight() {
return uILight;
public boolean isUILight() {
return uiLight;
}
public boolean isSafetyLock() {
@ -464,6 +345,10 @@ public class ElectroluxPureA9DTO {
return signalStrength;
}
public Map<String, Object> getMetadata() {
return metadata;
}
public int getVersion() {
return version;
}
@ -472,24 +357,12 @@ public class ElectroluxPureA9DTO {
return deviceId;
}
public int getpM1() {
return pM1;
public int getCO2() {
return co2;
}
public int getpM25() {
return pM25;
}
public int getpM10() {
return pM10;
}
public int gettVOC() {
return tVOC;
}
public int getcO2() {
return cO2;
public int getTVOC() {
return tvoc;
}
public int getTemp() {
@ -500,82 +373,20 @@ public class ElectroluxPureA9DTO {
return humidity;
}
public int getEnvLightLvl() {
return envLightLvl;
public int getRSSI() {
return rssi;
}
public int getrSSI() {
return rSSI;
}
}
public class SafetyLock {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class Scheduler {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class SignalStrength {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class Sleep {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class TVOC {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class Temp {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class TimeZoneStandardName {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
@SerializedName("$lastUpdatedVersion")
public int lastUpdatedVersion;
}
public class Twin {
public String deviceId = "";
public Properties properties = new Properties();
public String status = "";
public String connectionState = "";
public String getDeviceId() {
return deviceId;
public int getPM1() {
return pm1;
}
public Properties getProperties() {
return properties;
public int getPM25() {
return pm25;
}
public String getStatus() {
return status;
public int getPM10() {
return pm10;
}
public String getConnectionState() {
return connectionState;
}
}
public class UILight {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
public class Workmode {
@SerializedName("$lastUpdated")
public String lastUpdated = "";
}
}

View File

@ -12,7 +12,7 @@
*/
package org.openhab.binding.electroluxair.internal.handler;
import static org.openhab.binding.electroluxair.internal.ElectroluxAirBindingConstants.THING_TYPE_BRIDGE;
import static org.openhab.binding.electroluxair.internal.ElectroluxAirBindingConstants.*;
import java.util.Collection;
import java.util.Collections;
@ -37,6 +37,7 @@ import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import com.google.gson.Gson;
@ -147,6 +148,8 @@ public class ElectroluxAirBridgeHandler extends BaseBridgeHandler {
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
return;
if (CHANNEL_STATUS.equals(channelUID.getId()) && command instanceof RefreshType) {
scheduler.schedule(this::refreshAndUpdateStatus, 1, TimeUnit.SECONDS);
}
}
}

View File

@ -22,6 +22,7 @@ import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.electroluxair.internal.ElectroluxAirBindingConstants;
import org.openhab.binding.electroluxair.internal.ElectroluxAirConfiguration;
import org.openhab.binding.electroluxair.internal.api.ElectroluxDeltaAPI;
import org.openhab.binding.electroluxair.internal.dto.ElectroluxPureA9DTO;
@ -38,6 +39,7 @@ import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BridgeHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
@ -66,30 +68,46 @@ public class ElectroluxAirHandler extends BaseThingHandler {
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("Command received: {}", command);
if (CHANNEL_STATUS.equals(channelUID.getId()) || command instanceof RefreshType) {
update();
Bridge bridge = getBridge();
if (bridge != null) {
BridgeHandler bridgeHandler = bridge.getHandler();
if (bridgeHandler != null) {
bridgeHandler.handleCommand(channelUID, command);
}
}
} else {
ElectroluxPureA9DTO dto = getElectroluxPureA9DTO();
ElectroluxDeltaAPI api = getElectroluxDeltaAPO();
ElectroluxDeltaAPI api = getElectroluxDeltaAPI();
if (api != null && dto != null) {
if (CHANNEL_WORK_MODE.equals(channelUID.getId())) {
if (command.toString().equals(COMMAND_WORKMODE_POWEROFF)) {
api.workModePowerOff(dto.getPncId());
api.workModePowerOff(dto.getApplianceId());
} else if (command.toString().equals(COMMAND_WORKMODE_AUTO)) {
api.workModeAuto(dto.getPncId());
api.workModeAuto(dto.getApplianceId());
} else if (command.toString().equals(COMMAND_WORKMODE_MANUAL)) {
api.workModeManual(dto.getPncId());
api.workModeManual(dto.getApplianceId());
}
} else if (CHANNEL_FAN_SPEED.equals(channelUID.getId())) {
api.setFanSpeedLevel(dto.getPncId(), Integer.parseInt(command.toString()));
api.setFanSpeedLevel(dto.getApplianceId(), Integer.parseInt(command.toString()));
} else if (CHANNEL_IONIZER.equals(channelUID.getId())) {
if (command == OnOffType.OFF) {
api.setIonizer(dto.getPncId(), "false");
api.setIonizer(dto.getApplianceId(), "false");
} else if (command == OnOffType.ON) {
api.setIonizer(dto.getPncId(), "true");
api.setIonizer(dto.getApplianceId(), "true");
} else {
logger.debug("Unknown command! {}", command);
}
}
Bridge bridge = getBridge();
if (bridge != null) {
BridgeHandler bridgeHandler = bridge.getHandler();
if (bridgeHandler != null) {
bridgeHandler.handleCommand(
new ChannelUID(this.thing.getUID(), ElectroluxAirBindingConstants.CHANNEL_STATUS),
RefreshType.REFRESH);
}
}
}
}
}
@ -115,7 +133,7 @@ public class ElectroluxAirHandler extends BaseThingHandler {
}
}
private @Nullable ElectroluxDeltaAPI getElectroluxDeltaAPO() {
private @Nullable ElectroluxDeltaAPI getElectroluxDeltaAPI() {
Bridge bridge = getBridge();
if (bridge != null) {
ElectroluxAirBridgeHandler handler = (ElectroluxAirBridgeHandler) bridge.getHandler();
@ -143,6 +161,7 @@ public class ElectroluxAirHandler extends BaseThingHandler {
getThing().getChannels().stream().map(Channel::getUID).filter(channelUID -> isLinked(channelUID))
.forEach(channelUID -> {
State state = getValue(channelUID.getId(), dto);
logger.trace("Channel: {}, State: {}", channelUID, state);
updateState(channelUID, state);
});
updateStatus(ThingStatus.ONLINE);
@ -152,38 +171,35 @@ public class ElectroluxAirHandler extends BaseThingHandler {
private State getValue(String channelId, ElectroluxPureA9DTO dto) {
switch (channelId) {
case CHANNEL_TEMPERATURE:
return new QuantityType<Temperature>(dto.getTwin().getProperties().getReported().getTemp(),
SIUnits.CELSIUS);
return new QuantityType<Temperature>(dto.getProperties().getReported().getTemp(), SIUnits.CELSIUS);
case CHANNEL_HUMIDITY:
return new QuantityType<Dimensionless>(dto.getTwin().getProperties().getReported().getHumidity(),
Units.PERCENT);
return new QuantityType<Dimensionless>(dto.getProperties().getReported().getHumidity(), Units.PERCENT);
case CHANNEL_TVOC:
return new QuantityType<Density>(dto.getTwin().getProperties().getReported().gettVOC(),
return new QuantityType<Density>(dto.getProperties().getReported().getTVOC(),
Units.MICROGRAM_PER_CUBICMETRE);
case CHANNEL_PM1:
return new QuantityType<Dimensionless>(dto.getTwin().getProperties().getReported().getpM1(),
return new QuantityType<Dimensionless>(dto.getProperties().getReported().getPM1(),
Units.PARTS_PER_BILLION);
case CHANNEL_PM25:
return new QuantityType<Dimensionless>(dto.getTwin().getProperties().getReported().getpM25(),
return new QuantityType<Dimensionless>(dto.getProperties().getReported().getPM25(),
Units.PARTS_PER_BILLION);
case CHANNEL_PM10:
return new QuantityType<Dimensionless>(dto.getTwin().getProperties().getReported().getpM10(),
return new QuantityType<Dimensionless>(dto.getProperties().getReported().getPM10(),
Units.PARTS_PER_BILLION);
case CHANNEL_CO2:
return new QuantityType<Dimensionless>(dto.getTwin().getProperties().getReported().getcO2(),
return new QuantityType<Dimensionless>(dto.getProperties().getReported().getCO2(),
Units.PARTS_PER_MILLION);
case CHANNEL_FAN_SPEED:
return new StringType(Integer.toString(dto.getTwin().getProperties().getReported().getFanspeed()));
return new StringType(Integer.toString(dto.getProperties().getReported().getFanspeed()));
case CHANNEL_FILTER_LIFE:
return new QuantityType<Dimensionless>(dto.getTwin().getProperties().getReported().getFilterLife(),
return new QuantityType<Dimensionless>(dto.getProperties().getReported().getFilterLife(),
Units.PERCENT);
case CHANNEL_IONIZER:
return OnOffType.from(dto.getTwin().getProperties().getReported().ionizer);
return OnOffType.from(dto.getProperties().getReported().isIonizer());
case CHANNEL_WORK_MODE:
return new StringType(dto.getTwin().getProperties().getReported().workmode);
return new StringType(dto.getProperties().getReported().getWorkmode());
case CHANNEL_DOOR_OPEN:
return dto.getTwin().getProperties().getReported().doorOpen ? OpenClosedType.OPEN
: OpenClosedType.CLOSED;
return dto.getProperties().getReported().isDoorOpen() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
}
return UnDefType.UNDEF;
}
@ -196,13 +212,12 @@ public class ElectroluxAirHandler extends BaseThingHandler {
if (bridgeHandler != null) {
ElectroluxPureA9DTO dto = bridgeHandler.getElectroluxAirThings().get(config.getDeviceId());
if (dto != null) {
properties.put(Thing.PROPERTY_VENDOR, dto.getApplicancesInfo().brand);
properties.put(PROPERTY_COLOUR, dto.getApplicancesInfo().colour);
properties.put(PROPERTY_DEVICE, dto.getApplicancesInfo().device);
properties.put(Thing.PROPERTY_MODEL_ID, dto.getApplicancesInfo().model);
properties.put(Thing.PROPERTY_SERIAL_NUMBER, dto.getApplicancesInfo().serialNumber);
properties.put(Thing.PROPERTY_FIRMWARE_VERSION,
dto.getTwin().getProperties().getReported().frmVerNIU);
properties.put(Thing.PROPERTY_VENDOR, dto.getApplianceInfo().getBrand());
properties.put(PROPERTY_COLOUR, dto.getApplianceInfo().getColour());
properties.put(PROPERTY_DEVICE, dto.getApplianceInfo().getDeviceType());
properties.put(Thing.PROPERTY_MODEL_ID, dto.getApplianceInfo().getModel());
properties.put(Thing.PROPERTY_SERIAL_NUMBER, dto.getApplianceInfo().getSerialNumber());
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, dto.getProperties().getReported().getFrmVerNIU());
}
}
}

View File

@ -181,5 +181,4 @@
<description>Ionizer Status</description>
</channel-type>
</thing:thing-descriptions>