This is an openHAB binding for the Pure A9 Air Purifier, by Electrolux.
-This binding uses the Electrolux Delta REST API.
-

## Supported Things
| 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
The following channels are supported:
-| Channel Type ID | Item Type | Description |
-|-----------------------------|-----------------------|------------------------------------------------------------------------------|
-| temperature | Number:Temperature | This channel reports the current temperature. |
-| humidity | Number:Dimensionless | This channel reports the current humidity in percentage. |
-| tvoc | Number:Density | This channel reports the total Volatile Organic Compounds in microgram/m3. |
-| pm1 | Number:Dimensionless | This channel reports the Particulate Matter 1 in ppb. |
-| pm2_5 | Number:Dimensionless | This channel reports the Particulate Matter 2.5 in ppb. |
-| pm10 | Number:Dimensionless | This channel reports the Particulate Matter 10 in ppb. |
-| 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). |
-| 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.)|
+| Channel Type ID | Item Type | Description |
+|-----------------------------|-----------------------|--------------------------------------------------------------------------------|
+| temperature | Number:Temperature | This channel reports the current temperature. |
+| humidity | Number:Dimensionless | This channel reports the current humidity in percentage. |
+| tvoc | Number:Density | This channel reports the total Volatile Organic Compounds in microgram/m3. |
+| pm1 | Number:Dimensionless | This channel reports the Particulate Matter 1 in ppb. |
+| pm2_5 | Number:Dimensionless | This channel reports the Particulate Matter 2.5 in ppb. |
+| pm10 | Number:Dimensionless | This channel reports the Particulate Matter 10 in ppb. |
+| 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). |
+| 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.) |
+| uiLIght | Switch | This channel sets and reports the status of the UI Light function (On/Off). |
+| safetyLock | Switch | This channel sets and reports the status of the Safety Lock function (On/Off).|
## Full Example
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"}
+// UI Light
+Switch ElectroluxAirUILight "Electrolux Air UI Light Setting" {channel="electroluxair:electroluxpurea9:myAPI:myElectroluxPureA9:uiLight"}
+// Ionizer
+Switch ElectroluxAirIonizer "Electrolux Air Ionizer Setting" {channel="electroluxair:electroluxpurea9:myAPI:myElectroluxPureA9:ionizer"}
+// Safety Lock
+Switch ElectroluxAirSafetyLock "Electrolux Air Safety Lock Setting" {channel="electroluxair:electroluxpurea9:myAPI:myElectroluxPureA9:safetyLock"}
```
public static final String CHANNEL_FAN_SPEED = "fanSpeed";
public static final String CHANNEL_WORK_MODE = "workMode";
public static final String CHANNEL_IONIZER = "ionizer";
+ public static final String CHANNEL_UI_LIGHT = "uiLight";
+ public static final String CHANNEL_SAFETY_LOCK = "safetyLock";
// List of all Properties ids
public static final String PROPERTY_BRAND = "brand";
*/
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;
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
*/
@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);
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;
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());
}
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);
+ public boolean setUILight(String applianceId, String uiLightStatus) {
+ String commandJSON = "{ \"UILight\": " + uiLightStatus + "}";
+ try {
+ return sendCommand(commandJSON, applianceId);
+ } catch (ElectroluxAirException e) {
+ logger.warn("Work mode manual failed {}", e.getMessage());
+ }
+ return false;
+ }
+
+ public boolean setSafetyLock(String applianceId, String safetyLockStatus) {
+ String commandJSON = "{ \"SafetyLock\": " + safetyLockStatus + "}";
+ try {
+ return sendCommand(commandJSON, applianceId);
+ } catch (ElectroluxAirException e) {
+ logger.warn("Work mode manual failed {}", e.getMessage());
+ }
+ return false;
+ }
+
+ 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());
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);
}
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();
}
private String getAppliances() throws ElectroluxAirException {
- String uri = "Domains/Appliances";
- try {
- return getFromApi(uri);
- } catch (ElectroluxAirException | InterruptedException e) {
- throw new ElectroluxAirException(e);
- }
- }
-
- private String getAppliancesInfo(String pncId) throws ElectroluxAirException {
- String uri = "AppliancesInfo/" + pncId;
try {
- return getFromApi(uri);
+ return getFromApi(APPLIANCES_URL);
} 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;
-
+ private String getAppliancesInfo(String applianceId) throws ElectroluxAirException {
try {
- json = getFromApi(uri);
+ return getFromApi(APPLIANCES_URL + "/" + applianceId + "/info");
} 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();
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");
}
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 = "";
- }
}
*/
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;
*/
@NonNullByDefault
public class ElectroluxPureA9DTO {
- public String pncId = "";
- public ApplianceData applianceData = new ApplianceData();
- public AppliancesInfo applicancesInfo = new AppliancesInfo();
-
- public Twin twin = new Twin();
- public String telemetry = "";
-
- public String getPncId() {
- return pncId;
- }
-
- public ApplianceData getApplianceData() {
- return applianceData;
- }
-
- public AppliancesInfo getApplicancesInfo() {
- return applicancesInfo;
- }
+ 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 void setApplicancesInfo(AppliancesInfo applicancesInfo) {
- this.applicancesInfo = applicancesInfo;
+ public String getApplianceId() {
+ return applianceId;
}
- public Twin getTwin() {
- return twin;
+ public ApplianceInfo getApplianceInfo() {
+ return applianceInfo;
}
- public String getTelemetry() {
- return telemetry;
- }
-
- public class MetaData1 {
-
- @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 class Metadata2 {
-
- @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 void setApplianceInfo(ApplianceInfo applianceInfo) {
+ this.applianceInfo = applianceInfo;
}
- public class ApplianceData {
-
- public String applianceName = "";
- public String created = "";
- public String modelName = "";
- public String pncId = "";
+ public ApplianceData getApplianceData() {
+ return applianceData;
}
- public class AppliancesInfo {
- public String brand = "";
- public String colour = "";
- public String device = "";
- public String model = "";
- public String serialNumber = "";
+ public Properties getProperties() {
+ return properties;
}
- public class CO2 {
- @SerializedName("$lastUpdated")
- public String lastUpdated3 = "";
+ public String getStatus() {
+ return status;
}
- 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 String getConnectionState() {
+ return connectionState;
}
- public class DoorOpen {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ 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 class DspIcoPM1 {
- @SerializedName("lastUpdated")
- public String lastUpdated = "";
- }
+ public String getManufacturingDateCode() {
+ return manufacturingDateCode;
+ }
- public class DspIcoPM10 {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getSerialNumber() {
+ return serialNumber;
+ }
- public class DspIcoPM25 {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getPnc() {
+ return pnc;
+ }
- public class DspIcoTVOC {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getBrand() {
+ return brand;
+ }
- public class EnvLightLvl {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getMarket() {
+ return market;
+ }
- public class ErrCommSensorDisplayBrd {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getProductArea() {
+ return productArea;
+ }
- public class ErrFanMtr {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getDeviceType() {
+ return deviceType;
+ }
- public class ErrPM25 {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getProject() {
+ return project;
+ }
- public class ErrRFID {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getModel() {
+ return model;
+ }
- public class ErrTVOC {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getVariant() {
+ return variant;
+ }
- public class ErrTempHumidity {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
+ public String getColour() {
+ return colour;
+ }
}
- public class Fanspeed {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ class ApplianceData {
+ private String applianceName = "";
+ private String created = "";
+ private String modelName = "";
- public class FilterLife {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getApplianceName() {
+ return applianceName;
+ }
- public class FilterRFID {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getCreated() {
+ return created;
+ }
- public class FilterType {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
+ public String getModelName() {
+ return modelName;
+ }
}
- public class FrmVerNIU {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- @SerializedName("$lastUpdatedVersion")
- public int lastUpdatedVersion;
- }
+ public class Properties {
+ private Desired desired = new Desired();
+ private Reported reported = new Reported();
+ private Object metadata = new Object();
- // public class FrmVerNIU_ {
- // @SerializedName("$lastUpdated")
- // public String lastUpdated = "";
- // }
+ public Desired getDesired() {
+ return desired;
+ }
- public class Humidity {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public Reported getReported() {
+ return reported;
+ }
- public class Ionizer {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
+ public Object getMetadata() {
+ return metadata;
+ }
}
- public class PM1 {
- @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 class PM10 {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getTimeZoneStandardName() {
+ return timeZoneStandardName;
+ }
- public class PM25 {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
+ public String getFrmVerNIU() {
+ return frmVerNIU;
+ }
- public class Properties {
- public Desired desired = new Desired();
- public Reported reported = new Reported();
+ public boolean isLocationReq() {
+ return locationReq;
+ }
- public Reported getReported() {
- return reported;
+ public Map<String, Object> getMetadata() {
+ return metadata;
}
- }
- public class RSSI {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
+ 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;
}
public int getFanspeed() {
- return fanspeed;
+ return fanSpeed;
}
- public boolean isuILight() {
- return uILight;
+ public boolean isUILight() {
+ return uiLight;
}
public boolean isSafetyLock() {
return signalStrength;
}
+ public Map<String, Object> getMetadata() {
+ return metadata;
+ }
+
public int getVersion() {
return version;
}
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() {
return humidity;
}
- public int getEnvLightLvl() {
- return envLightLvl;
+ public int getRSSI() {
+ return rssi;
}
- public int getrSSI() {
- return rSSI;
+ public int getPM1() {
+ return pm1;
}
- }
- 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 Properties getProperties() {
- return properties;
- }
-
- public String getStatus() {
- return status;
+ public int getPM25() {
+ return pm25;
}
- public String getConnectionState() {
- return connectionState;
+ public int getPM10() {
+ return pm10;
}
}
-
- public class UILight {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
-
- public class Workmode {
- @SerializedName("$lastUpdated")
- public String lastUpdated = "";
- }
}
*/
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;
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;
@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);
+ }
}
}
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;
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;
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);
+ }
+ } else if (CHANNEL_UI_LIGHT.equals(channelUID.getId())) {
+ if (command == OnOffType.OFF) {
+ api.setUILight(dto.getApplianceId(), "false");
+ } else if (command == OnOffType.ON) {
+ api.setUILight(dto.getApplianceId(), "true");
+ } else {
+ logger.debug("Unknown command! {}", command);
+ }
+ } else if (CHANNEL_SAFETY_LOCK.equals(channelUID.getId())) {
+ if (command == OnOffType.OFF) {
+ api.setSafetyLock(dto.getApplianceId(), "false");
+ } else if (command == OnOffType.ON) {
+ api.setSafetyLock(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);
+ }
+ }
}
}
}
}
}
- private @Nullable ElectroluxDeltaAPI getElectroluxDeltaAPO() {
+ private @Nullable ElectroluxDeltaAPI getElectroluxDeltaAPI() {
Bridge bridge = getBridge();
if (bridge != null) {
ElectroluxAirBridgeHandler handler = (ElectroluxAirBridgeHandler) bridge.getHandler();
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);
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_UI_LIGHT:
+ return OnOffType.from(dto.getProperties().getReported().isUILight());
+ case CHANNEL_SAFETY_LOCK:
+ return OnOffType.from(dto.getProperties().getReported().isSafetyLock());
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;
}
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());
}
}
}
channel-type.electroluxair.pm10.description = Particulate Matter 10 (0.01mm)
channel-type.electroluxair.pm2_5.label = PM2.5
channel-type.electroluxair.pm2_5.description = Particulate Matter 2.5 (0.0025mm)
+channel-type.electroluxair.safetyLock.label = Safety Lock
+channel-type.electroluxair.safetyLock.description = Safety Lock Status
channel-type.electroluxair.status.label = Current Status
channel-type.electroluxair.status.description = Information on current status.
channel-type.electroluxair.temperature.label = Temperature
channel-type.electroluxair.temperature.description = Temperature
channel-type.electroluxair.tvoc.label = TVOC
channel-type.electroluxair.tvoc.description = Total Volatile Organic Compounds
+channel-type.electroluxair.uiLight.label = UI Light
+channel-type.electroluxair.uiLight.description = Air Quality Light Status
channel-type.electroluxair.workMode.label = Work Mode Setting
channel-type.electroluxair.workMode.description = Work Mode Setting
channel-type.electroluxair.workMode.state.option.PowerOff = Power Off
<channel id="fanSpeed" typeId="fanSpeed"/>
<channel id="workMode" typeId="workMode"/>
<channel id="ionizer" typeId="ionizer"/>
+ <channel id="uiLight" typeId="uiLight"/>
+ <channel id="safetyLock" typeId="safetyLock"/>
<channel id="status" typeId="status"/>
</channels>
<properties>
<property name="vendor">Electrolux</property>
+ <property name="thingTypeVersion">1</property>
</properties>
<representation-property>deviceId</representation-property>
<description>Ionizer Status</description>
</channel-type>
+ <channel-type id="uiLight">
+ <item-type>Switch</item-type>
+ <label>UI Light</label>
+ <description>Air Quality Light Status</description>
+ </channel-type>
+
+ <channel-type id="safetyLock">
+ <item-type>Switch</item-type>
+ <label>Safety Lock</label>
+ <description>Safety Lock Status</description>
+ </channel-type>
+
</thing:thing-descriptions>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
+
+ <thing-type uid="electroluxair:electroluxpurea9">
+ <instruction-set targetVersion="1">
+ <add-channel id="uiLight">
+ <type>electroluxair:uiLight</type>
+ </add-channel>
+ <add-channel id="safetyLock">
+ <type>electroluxair:safetyLock</type>
+ </add-channel>
+ </instruction-set>
+ </thing-type>
+
+</update:update-descriptions>