[homeconnect] Add power state support for the washing machines (#18634)
* Add power state support for the washing machines.(#18633) Signed-off-by: Philipp Schneider <philipp.schneider@nixo-soft.de>pull/18742/head
parent
9409800d4f
commit
aa60331a8e
|
@ -18,7 +18,7 @@ Supported devices: dishwasher, washer, washer / dryer combination, dryer, oven,
|
|||
#### experimental support
|
||||
|
||||
| Home appliance | Thing Type ID |
|
||||
| --------------- | ------------ |
|
||||
| -------------------------- | ------------- |
|
||||
| Dishwasher | dishwasher |
|
||||
| Washer | washer |
|
||||
| Washer / Dryer combination | washerdryer |
|
||||
|
@ -39,7 +39,7 @@ After the bridge has been added and authorized, devices are discovered automatic
|
|||
|
||||
| Channel Type ID | Item Type | Read only | Description | Available on thing |
|
||||
| --------------- | --------- | --------- | ----------- | ------------------ |
|
||||
| power_state | Switch | false | This setting describes the current power state of the home appliance. | dishwasher, oven, coffeemaker, hood, hob |
|
||||
| power_state | Switch | false | This setting describes the current power state of the home appliance. | dishwasher, oven, coffeemaker, hood, hob, washer, washerdryer |
|
||||
| door_state | Contact | true | This status describes the door state of a home appliance. A status change is either triggered by the user operating the home appliance locally (i.e. opening/closing door) or automatically by the home appliance (i.e. locking the door). | dishwasher, washer, washerdryer, dryer, oven, fridgefreezer |
|
||||
| operation_state | String | true | This status describes the operation state of the home appliance. | dishwasher, washer, washerdryer, dryer, oven, hood, hob, coffeemaker |
|
||||
| remote_start_allowance_state | Switch | true | This status indicates whether the remote program start is enabled. This can happen due to a programmatic change (only disabling), or manually by the user changing the flag locally on the home appliance, or automatically after a certain duration - usually in 24 hours. | dishwasher, washer, washerdryer, dryer, oven, hood, coffeemaker |
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.openhab.binding.homeconnect.internal.client.model.HomeAppliance;
|
|||
import org.openhab.binding.homeconnect.internal.client.model.HomeConnectRequest;
|
||||
import org.openhab.binding.homeconnect.internal.client.model.HomeConnectResponse;
|
||||
import org.openhab.binding.homeconnect.internal.client.model.Option;
|
||||
import org.openhab.binding.homeconnect.internal.client.model.PowerStateAccess;
|
||||
import org.openhab.binding.homeconnect.internal.client.model.Program;
|
||||
import org.openhab.binding.homeconnect.internal.configuration.ApiBridgeConfiguration;
|
||||
import org.openhab.core.auth.client.oauth2.OAuthClientService;
|
||||
|
@ -53,6 +54,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
|
@ -324,6 +326,54 @@ public class HomeConnectApiClient {
|
|||
return getSetting(haId, SETTING_POWER_STATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides information on whether the power state of device can be set or only read.
|
||||
*
|
||||
* @param haId home appliance id
|
||||
* @return {@link PowerStateAccess}
|
||||
* @throws CommunicationException API communication exception
|
||||
* @throws AuthorizationException oAuth authorization exception
|
||||
* @throws ApplianceOfflineException appliance is not connected to the cloud
|
||||
*/
|
||||
public PowerStateAccess getPowerStateAccess(String haId)
|
||||
throws CommunicationException, AuthorizationException, ApplianceOfflineException {
|
||||
|
||||
String powerStateSettings = getRaw(haId, BASE_PATH + haId + "/settings/" + SETTING_POWER_STATE);
|
||||
|
||||
/***
|
||||
* Example response:
|
||||
* {
|
||||
* "data": {
|
||||
* "key": "BSH.Common.Setting.PowerState",
|
||||
* "value": "BSH.Common.EnumType.PowerState.Off",
|
||||
* "type": "BSH.Common.EnumType.PowerState",
|
||||
* "constraints": {
|
||||
* "allowedvalues": [
|
||||
* "BSH.Common.EnumType.PowerState.Off",
|
||||
* "BSH.Common.EnumType.PowerState.On"
|
||||
* ],
|
||||
* "default": "BSH.Common.EnumType.PowerState.On",
|
||||
* "access": "readWrite"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
if (powerStateSettings != null) {
|
||||
JsonObject responseObject = parseString(powerStateSettings).getAsJsonObject();
|
||||
JsonObject data = responseObject.getAsJsonObject("data");
|
||||
JsonElement jsonConstraints = data.get("constraints");
|
||||
if (jsonConstraints.isJsonObject()) {
|
||||
JsonElement jsonAccess = jsonConstraints.getAsJsonObject().get("access");
|
||||
if (jsonAccess.isJsonPrimitive()) {
|
||||
return PowerStateAccess.fromString(jsonAccess.getAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PowerStateAccess.READ_ONLY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set power state of device.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.homeconnect.internal.client.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PowerStateAccess} enum defines the access types for the power state of the device.
|
||||
*
|
||||
* @author Philipp Schneider - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum PowerStateAccess {
|
||||
|
||||
READ_ONLY,
|
||||
|
||||
READ_WRITE;
|
||||
|
||||
public static PowerStateAccess fromString(String access) {
|
||||
switch (access.toLowerCase()) {
|
||||
case "read":
|
||||
return READ_ONLY;
|
||||
case "readwrite":
|
||||
return READ_WRITE;
|
||||
default:
|
||||
// Default to READ_ONLY if the access type is not recognized
|
||||
return READ_ONLY;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -52,6 +52,7 @@ import org.openhab.binding.homeconnect.internal.client.model.Data;
|
|||
import org.openhab.binding.homeconnect.internal.client.model.Event;
|
||||
import org.openhab.binding.homeconnect.internal.client.model.HomeAppliance;
|
||||
import org.openhab.binding.homeconnect.internal.client.model.Option;
|
||||
import org.openhab.binding.homeconnect.internal.client.model.PowerStateAccess;
|
||||
import org.openhab.binding.homeconnect.internal.client.model.Program;
|
||||
import org.openhab.binding.homeconnect.internal.handler.cache.ExpiringStateMap;
|
||||
import org.openhab.binding.homeconnect.internal.type.HomeConnectDynamicStateDescriptionProvider;
|
||||
|
@ -1025,6 +1026,14 @@ public abstract class AbstractHomeConnectThingHandler extends BaseThingHandler i
|
|||
return (channelUID, cache) -> updateState(channelUID, cache.putIfAbsentAndGet(channelUID, () -> {
|
||||
Optional<HomeConnectApiClient> apiClient = getApiClient();
|
||||
if (apiClient.isPresent()) {
|
||||
|
||||
// set read-only state description, if device has read-only power state option
|
||||
Optional<Channel> powerStateChannel = getThingChannel(CHANNEL_POWER_STATE);
|
||||
if (powerStateChannel.isPresent()) {
|
||||
dynamicStateDescriptionProvider.withReadOnly(powerStateChannel.get().getUID(),
|
||||
apiClient.get().getPowerStateAccess(getThingHaId()) == PowerStateAccess.READ_ONLY);
|
||||
}
|
||||
|
||||
Data data = apiClient.get().getPowerState(getThingHaId());
|
||||
if (data.getValue() != null) {
|
||||
return OnOffType.from(STATE_POWER_ON.equals(data.getValue()));
|
||||
|
@ -1320,7 +1329,8 @@ public abstract class AbstractHomeConnectThingHandler extends BaseThingHandler i
|
|||
protected void handlePowerCommand(final ChannelUID channelUID, final Command command,
|
||||
final HomeConnectApiClient apiClient, String stateNotOn)
|
||||
throws CommunicationException, AuthorizationException, ApplianceOfflineException {
|
||||
if (command instanceof OnOffType && CHANNEL_POWER_STATE.equals(channelUID.getId())) {
|
||||
if (command instanceof OnOffType && CHANNEL_POWER_STATE.equals(channelUID.getId())
|
||||
&& apiClient.getPowerStateAccess(getThingHaId()) == PowerStateAccess.READ_WRITE) {
|
||||
apiClient.setPowerState(getThingHaId(), OnOffType.ON.equals(command) ? STATE_POWER_ON : stateNotOn);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ public class HomeConnectWasherDryerHandler extends AbstractHomeConnectThingHandl
|
|||
@Override
|
||||
protected void configureChannelUpdateHandlers(Map<String, ChannelUpdateHandler> handlers) {
|
||||
// register default update handlers
|
||||
handlers.put(CHANNEL_POWER_STATE, defaultPowerStateChannelUpdateHandler());
|
||||
handlers.put(CHANNEL_DOOR_STATE, defaultDoorStateChannelUpdateHandler());
|
||||
handlers.put(CHANNEL_OPERATION_STATE, defaultOperationStateChannelUpdateHandler());
|
||||
handlers.put(CHANNEL_REMOTE_CONTROL_ACTIVE_STATE, defaultRemoteControlActiveStateChannelUpdateHandler());
|
||||
|
@ -93,6 +94,7 @@ public class HomeConnectWasherDryerHandler extends AbstractHomeConnectThingHandl
|
|||
@Override
|
||||
protected void configureEventHandlers(Map<String, EventHandler> handlers) {
|
||||
// register default event handlers
|
||||
handlers.put(EVENT_POWER_STATE, defaultPowerStateEventHandler());
|
||||
handlers.put(EVENT_DOOR_STATE, defaultDoorStateEventHandler());
|
||||
handlers.put(EVENT_REMOTE_CONTROL_ACTIVE, updateRemoteControlActiveAndProgramOptionsStateEventHandler());
|
||||
handlers.put(EVENT_REMOTE_CONTROL_START_ALLOWED,
|
||||
|
@ -137,6 +139,9 @@ public class HomeConnectWasherDryerHandler extends AbstractHomeConnectThingHandl
|
|||
final HomeConnectApiClient apiClient)
|
||||
throws CommunicationException, AuthorizationException, ApplianceOfflineException {
|
||||
super.handleCommand(channelUID, command, apiClient);
|
||||
|
||||
handlePowerCommand(channelUID, command, apiClient, STATE_POWER_OFF);
|
||||
|
||||
String operationState = getOperationState();
|
||||
|
||||
// only handle these commands if operation state allows it
|
||||
|
|
|
@ -54,6 +54,7 @@ public class HomeConnectWasherHandler extends AbstractHomeConnectThingHandler {
|
|||
@Override
|
||||
protected void configureChannelUpdateHandlers(Map<String, ChannelUpdateHandler> handlers) {
|
||||
// register default update handlers
|
||||
handlers.put(CHANNEL_POWER_STATE, defaultPowerStateChannelUpdateHandler());
|
||||
handlers.put(CHANNEL_DOOR_STATE, defaultDoorStateChannelUpdateHandler());
|
||||
handlers.put(CHANNEL_OPERATION_STATE, defaultOperationStateChannelUpdateHandler());
|
||||
handlers.put(CHANNEL_REMOTE_CONTROL_ACTIVE_STATE, defaultRemoteControlActiveStateChannelUpdateHandler());
|
||||
|
@ -99,6 +100,7 @@ public class HomeConnectWasherHandler extends AbstractHomeConnectThingHandler {
|
|||
@Override
|
||||
protected void configureEventHandlers(Map<String, EventHandler> handlers) {
|
||||
// register default event handlers
|
||||
handlers.put(EVENT_POWER_STATE, defaultPowerStateEventHandler());
|
||||
handlers.put(EVENT_DOOR_STATE, defaultDoorStateEventHandler());
|
||||
handlers.put(EVENT_REMOTE_CONTROL_ACTIVE, updateRemoteControlActiveAndProgramOptionsStateEventHandler());
|
||||
handlers.put(EVENT_REMOTE_CONTROL_START_ALLOWED,
|
||||
|
@ -186,6 +188,9 @@ public class HomeConnectWasherHandler extends AbstractHomeConnectThingHandler {
|
|||
final HomeConnectApiClient apiClient)
|
||||
throws CommunicationException, AuthorizationException, ApplianceOfflineException {
|
||||
super.handleCommand(channelUID, command, apiClient);
|
||||
|
||||
handlePowerCommand(channelUID, command, apiClient, STATE_POWER_OFF);
|
||||
|
||||
String operationState = getOperationState();
|
||||
|
||||
// only handle these commands if operation state allows it
|
||||
|
|
|
@ -12,12 +12,26 @@
|
|||
*/
|
||||
package org.openhab.binding.homeconnect.internal.type;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
|
||||
import org.openhab.core.thing.events.ThingEventFactory;
|
||||
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
|
||||
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
|
||||
import org.openhab.core.thing.type.ChannelType;
|
||||
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
|
||||
import org.openhab.core.types.StateDescription;
|
||||
import org.openhab.core.types.StateDescriptionFragmentBuilder;
|
||||
import org.openhab.core.types.StateOption;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
@ -31,6 +45,8 @@ import org.osgi.service.component.annotations.Reference;
|
|||
@NonNullByDefault
|
||||
public class HomeConnectDynamicStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
|
||||
|
||||
protected final Map<ChannelUID, Boolean> channelReadOnlyMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Activate
|
||||
public HomeConnectDynamicStateDescriptionProvider(final @Reference EventPublisher eventPublisher, //
|
||||
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, //
|
||||
|
@ -39,4 +55,54 @@ public class HomeConnectDynamicStateDescriptionProvider extends BaseDynamicState
|
|||
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
|
||||
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given {@link ChannelUID}, set a readyOnly flag that should be used for the channel, instead of the one
|
||||
* defined statically in the {@link ChannelType}.
|
||||
*
|
||||
* @param channelUID the {@link ChannelUID} of the channel
|
||||
* @param readOnly readOnly flag
|
||||
*/
|
||||
public void withReadOnly(ChannelUID channelUID, boolean readOnly) {
|
||||
Boolean oldReadOnly = channelReadOnlyMap.get(channelUID);
|
||||
if (oldReadOnly == null || oldReadOnly != readOnly) {
|
||||
channelReadOnlyMap.put(channelUID, readOnly);
|
||||
postEvent(ThingEventFactory.createChannelDescriptionChangedEvent(channelUID,
|
||||
itemChannelLinkRegistry != null ? itemChannelLinkRegistry.getLinkedItemNames(channelUID) : Set.of(),
|
||||
StateDescriptionFragmentBuilder.create().withReadOnly(readOnly).build(), null));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original,
|
||||
@Nullable Locale locale) {
|
||||
// can be overridden by subclasses
|
||||
ChannelUID channelUID = channel.getUID();
|
||||
String pattern = channelPatternMap.get(channelUID);
|
||||
List<StateOption> options = channelOptionsMap.get(channelUID);
|
||||
Boolean readOnly = channelReadOnlyMap.get(channelUID);
|
||||
if (pattern == null && options == null && readOnly == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StateDescriptionFragmentBuilder builder = (original == null) ? StateDescriptionFragmentBuilder.create()
|
||||
: StateDescriptionFragmentBuilder.create(original);
|
||||
|
||||
if (pattern != null) {
|
||||
String localizedPattern = localizeStatePattern(pattern, channel, locale);
|
||||
if (localizedPattern != null) {
|
||||
builder.withPattern(localizedPattern);
|
||||
}
|
||||
}
|
||||
|
||||
if (options != null) {
|
||||
builder.withOptions(localizedStateOptions(options, channel, locale));
|
||||
}
|
||||
|
||||
if (readOnly != null) {
|
||||
builder.withReadOnly(readOnly);
|
||||
}
|
||||
|
||||
return builder.build().toStateDescription();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
<description>Home Connect connected washing machine (e.g. Bosch or Siemens).</description>
|
||||
<semantic-equipment-tag>WashingMachine</semantic-equipment-tag>
|
||||
<channels>
|
||||
<channel id="power_state" typeId="system.power"/>
|
||||
<channel id="door_state" typeId="door_state"/>
|
||||
<channel id="operation_state" typeId="operation_state"/>
|
||||
<channel id="remote_start_allowance_state" typeId="remote_start_allowance_state"/>
|
||||
|
@ -129,6 +130,9 @@
|
|||
<channel id="remaining_program_time_state" typeId="remaining_program_time_state"/>
|
||||
<channel id="program_progress_state" typeId="program_progress_state"/>
|
||||
</channels>
|
||||
<properties>
|
||||
<property name="thingTypeVersion">1</property>
|
||||
</properties>
|
||||
<representation-property>haId</representation-property>
|
||||
<config-description>
|
||||
<parameter name="haId" type="text" required="true">
|
||||
|
@ -147,6 +151,7 @@
|
|||
<description>Home Connect connected combined washer dryer appliance.</description>
|
||||
<semantic-equipment-tag>WashingMachine</semantic-equipment-tag>
|
||||
<channels>
|
||||
<channel id="power_state" typeId="system.power"/>
|
||||
<channel id="door_state" typeId="door_state"/>
|
||||
<channel id="operation_state" typeId="operation_state"/>
|
||||
<channel id="remote_start_allowance_state" typeId="remote_start_allowance_state"/>
|
||||
|
@ -170,6 +175,9 @@
|
|||
<channel id="remaining_program_time_state" typeId="remaining_program_time_state"/>
|
||||
<channel id="program_progress_state" typeId="program_progress_state"/>
|
||||
</channels>
|
||||
<properties>
|
||||
<property name="thingTypeVersion">1</property>
|
||||
</properties>
|
||||
<representation-property>haId</representation-property>
|
||||
<config-description>
|
||||
<parameter name="haId" type="text" required="true">
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?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="homeconnect:washer">
|
||||
<instruction-set targetVersion="1">
|
||||
<add-channel id="power_state">
|
||||
<type>system:power</type>
|
||||
</add-channel>
|
||||
</instruction-set>
|
||||
</thing-type>
|
||||
|
||||
<thing-type uid="homeconnect:washerdryer">
|
||||
<instruction-set targetVersion="1">
|
||||
<add-channel id="power_state">
|
||||
<type>system:power</type>
|
||||
</add-channel>
|
||||
</instruction-set>
|
||||
</thing-type>
|
||||
|
||||
</update:update-descriptions>
|
Loading…
Reference in New Issue