Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[miio] Improve yeelight RGB with brightness, introduce substitutions #10984

Merged
merged 6 commits into from
Jul 17, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 60 additions & 60 deletions bundles/org.openhab.binding.miio/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
package org.openhab.binding.miio.internal.basic;

import java.awt.Color;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.PercentType;
import org.slf4j.Logger;
Expand Down Expand Up @@ -49,6 +51,31 @@ public static JsonElement bRGBtoHSV(JsonElement bRGB) throws ClassCastException
return bRGB;
}

/**
* Adds the brightness info (from separate channel) to a HSV value.
* *
*
* @param RGB
* @param map with device variables containing the brightness info
* @return HSV
*/
public static JsonElement addBrightToHSV(JsonElement rgbValue, @Nullable Map<String, Object> deviceVariables)
throws ClassCastException, IllegalStateException {
int bright = 100;
if (deviceVariables != null) {
JsonElement lastBright = (JsonElement) deviceVariables.getOrDefault("bright", new JsonPrimitive(100));
bright = lastBright.getAsInt();
}
if (rgbValue.isJsonPrimitive()
&& (rgbValue.getAsJsonPrimitive().isNumber() || rgbValue.getAsString().matches("^[0-9]+$"))) {
Color rgb = new Color(rgbValue.getAsInt());
HSBType hsb = HSBType.fromRGB(rgb.getRed(), rgb.getGreen(), rgb.getBlue());
hsb = new HSBType(hsb.getHue(), hsb.getSaturation(), new PercentType(bright));
return new JsonPrimitive(hsb.toFullString());
}
return rgbValue;
}

public static JsonElement secondsToHours(JsonElement seconds) throws ClassCastException {
double value = seconds.getAsDouble() / 3600;
return new JsonPrimitive(value);
Expand Down Expand Up @@ -94,9 +121,10 @@ public static JsonElement tankLevel(JsonElement value12) throws ClassCastExcepti
}
}

public static JsonElement execute(String transfortmation, JsonElement value) {
public static JsonElement execute(String transformation, JsonElement value,
@Nullable Map<String, Object> deviceVariables) {
try {
switch (transfortmation.toUpperCase()) {
switch (transformation.toUpperCase()) {
case "YEELIGHTSCENEID":
return yeelightSceneConversion(value);
case "SECONDSTOHOURS":
Expand All @@ -107,14 +135,16 @@ public static JsonElement execute(String transfortmation, JsonElement value) {
return divideHundred(value);
case "TANKLEVEL":
return tankLevel(value);
case "ADDBRIGHTTOHSV":
return addBrightToHSV(value, deviceVariables);
case "BRGBTOHSV":
return bRGBtoHSV(value);
default:
LOGGER.debug("Transformation {} not found. Returning '{}'", transfortmation, value.toString());
LOGGER.debug("Transformation {} not found. Returning '{}'", transformation, value.toString());
return value;
}
} catch (ClassCastException | IllegalStateException e) {
LOGGER.debug("Transformation {} failed. Returning '{}'", transfortmation, value.toString());
LOGGER.debug("Transformation {} failed. Returning '{}'", transformation, value.toString());
return value;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public void setStateDescription(@Nullable StateDescriptionDTO stateDescription)

public Boolean getRefresh() {
final @Nullable Boolean rf = refresh;
return rf != null && rf.booleanValue() && !getProperty().isEmpty();
return rf != null && rf.booleanValue();
}

public void setRefresh(@Nullable Boolean refresh) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import static org.openhab.binding.miio.internal.MiIoBindingConstants.*;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -71,6 +73,7 @@
public abstract class MiIoAbstractHandler extends BaseThingHandler implements MiIoMessageListener {
protected static final int MAX_QUEUE = 5;
protected static final Gson GSON = new GsonBuilder().create();
protected static final String TIMESTAMP = "timestamp";

protected ScheduledExecutorService miIoScheduler = scheduler;
protected @Nullable ScheduledFuture<?> pollingJob;
Expand Down Expand Up @@ -111,12 +114,13 @@ public MiIoAbstractHandler(Thing thing, MiIoDatabaseWatchService miIoDatabaseWat
public abstract void handleCommand(ChannelUID channelUID, Command command);

protected boolean handleCommandsChannels(ChannelUID channelUID, Command command) {
String cmd = processSubstitutions(command.toString(), deviceVariables);
if (channelUID.getId().equals(CHANNEL_COMMAND)) {
cmds.put(sendCommand(command.toString()), channelUID.getId());
cmds.put(sendCommand(cmd), channelUID.getId());
return true;
}
if (channelUID.getId().equals(CHANNEL_RPC)) {
cmds.put(sendCommand(command.toString(), cloudServer), channelUID.getId());
cmds.put(sendCommand(cmd, cloudServer), channelUID.getId());
return true;
}
return false;
Expand Down Expand Up @@ -146,6 +150,8 @@ public void initialize() {
}
this.cloudServer = configuration.cloudServer;
isIdentified = false;
deviceVariables.put(TIMESTAMP, Instant.now().getEpochSecond());
deviceVariables.put(PROPERTY_DID, configuration.deviceId);
miIoScheduler.schedule(this::initializeData, 1, TimeUnit.SECONDS);
int pollingPeriod = configuration.refreshInterval;
if (pollingPeriod > 0) {
Expand Down Expand Up @@ -216,14 +222,7 @@ protected int sendCommand(MiIoCommand command) {
}

protected int sendCommand(MiIoCommand command, String params) {
try {
final MiIoAsyncCommunication connection = getConnection();
return (connection != null) ? connection.queueCommand(command, params, getCloudServer()) : 0;
} catch (MiIoCryptoException | IOException e) {
logger.debug("Command {} for {} failed (type: {}): {}", command.toString(), getThing().getUID(),
getThing().getThingTypeUID(), e.getLocalizedMessage());
}
return 0;
return sendCommand(command.getCommand(), processSubstitutions(params, deviceVariables), getCloudServer(), "");
}

protected int sendCommand(String commandString) {
Expand All @@ -241,19 +240,39 @@ protected int sendCommand(String commandString) {
* @return vacuum response
*/
protected int sendCommand(String commandString, String cloudServer) {
final MiIoAsyncCommunication connection = getConnection();
String command = commandString.trim();
command = processSubstitutions(commandString.trim(), deviceVariables);
String param = "[]";
int sb = command.indexOf("[");
int cb = command.indexOf("{");
if (Math.max(sb, cb) > 0) {
int loc = (Math.min(sb, cb) > 0 ? Math.min(sb, cb) : Math.max(sb, cb));
param = command.substring(loc).trim();
command = command.substring(0, loc).trim();
}
return sendCommand(command, param, cloudServer, "");
}

protected int sendCommand(String command, String params, String cloudServer) {
return sendCommand(command, processSubstitutions(params, deviceVariables), cloudServer, "");
}

/**
* Sends commands to the {@link MiIoAsyncCommunication} for transmission to the Mi devices or cloud
*
* @param command (method) to be queued for execution
* @param parameters to be send with the command
* @param cloud server to be used or empty string for direct sending to the device
* @param sending subdevice or empty string for regular device
* @return message id
*/
protected int sendCommand(String command, String params, String cloudServer, String sender) {
try {
String command = commandString.trim();
String param = "[]";
int sb = command.indexOf("[");
int cb = command.indexOf("{");
if (Math.max(sb, cb) > 0) {
int loc = (Math.min(sb, cb) > 0 ? Math.min(sb, cb) : Math.max(sb, cb));
param = command.substring(loc).trim();
command = command.substring(0, loc).trim();
}
return (connection != null) ? connection.queueCommand(command, param, cloudServer) : 0;
final MiIoAsyncCommunication connection = getConnection();
return (connection != null) ? connection.queueCommand(command, params, cloudServer, sender) : 0;
} catch (MiIoCryptoException | IOException e) {
logger.debug("Command {} for {} failed (type: {}): {}", command.toString(), getThing().getUID(),
getThing().getThingTypeUID(), e.getLocalizedMessage());
disconnected(e.getMessage());
}
return 0;
Expand Down Expand Up @@ -414,6 +433,7 @@ private void updateDeviceIdConfig(String deviceId) {
Configuration config = editConfiguration();
config.put(PROPERTY_DID, deviceId);
updateConfiguration(config);
deviceVariables.put(PROPERTY_DID, deviceId);
} else {
logger.debug("Could not update config with deviceId: {}", deviceId);
}
Expand Down Expand Up @@ -458,6 +478,31 @@ private void updateProperties(JsonObject miioInfo) {
updateProperties(properties);
}

protected String processSubstitutions(String cmd, Map<String, Object> deviceVariables) {
String returnCmd = cmd;
String cmdParts[] = cmd.split("\\$");
for (String substitute : cmdParts) {
if (deviceVariables.containsKey(substitute)) {
String replacementString = "";
Object replacement = deviceVariables.get(substitute);
if (replacement == null) {
logger.debug("Replacement for '{}' is null. skipping replacement", substitute);
continue;
}
if (replacement instanceof Integer || replacement instanceof Long || replacement instanceof Double
|| replacement instanceof BigDecimal || replacement instanceof Boolean) {
replacementString = replacement.toString();
} else if (replacement instanceof String) {
replacementString = "\"" + (String) replacement + "\"";
} else {
replacementString = String.valueOf(replacement);
}
returnCmd = returnCmd.replace("$" + substitute + "$", replacementString);
}
}
return returnCmd;
}

protected boolean updateThingType(JsonObject miioInfo) {
MiIoBindingConfiguration configuration = getConfigAs(MiIoBindingConfiguration.class);
String model = miioInfo.get("model").getAsString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.awt.Color;
import java.io.IOException;
import java.net.URL;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
Expand Down Expand Up @@ -122,6 +123,7 @@ public void initialize() {
@Override
public void handleCommand(ChannelUID channelUID, Command receivedCommand) {
Command command = receivedCommand;
deviceVariables.put(TIMESTAMP, Instant.now().getEpochSecond());
if (command == RefreshType.REFRESH) {
if (updateDataCache.isExpired()) {
logger.debug("Refreshing {}", channelUID);
Expand Down Expand Up @@ -312,6 +314,7 @@ protected synchronized void updateData() {
}
final MiIoBasicDevice midevice = miioDevice;
if (midevice != null) {
deviceVariables.put(TIMESTAMP, Instant.now().getEpochSecond());
refreshProperties(midevice);
refreshCustomProperties(midevice);
refreshNetwork();
Expand Down Expand Up @@ -587,11 +590,12 @@ private void updateChannel(@Nullable MiIoBasicChannel basicChannel, String param
}
final String transformation = basicChannel.getTransformation();
if (transformation != null) {
JsonElement transformed = Conversions.execute(transformation, val);
JsonElement transformed = Conversions.execute(transformation, val, deviceVariables);
logger.debug("Transformed with '{}': {} {} -> {} ", transformation, basicChannel.getFriendlyName(), val,
transformed);
val = transformed;
}
deviceVariables.put(param, val);
try {
String[] chType = basicChannel.getType().toLowerCase().split(":");
switch (chType[0]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,7 @@ public synchronized void unregisterListener(MiIoMessageListener listener) {
}
}

public int queueCommand(MiIoCommand command, String cloudServer) throws MiIoCryptoException, IOException {
return queueCommand(command, "[]", cloudServer);
}

public int queueCommand(MiIoCommand command, String params, String cloudServer)
throws MiIoCryptoException, IOException {
return queueCommand(command.getCommand(), params, cloudServer);
}

public int queueCommand(String command, String params, String cloudServer)
public int queueCommand(String command, String params, String cloudServer, String sender)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: the String sender is used in the next coming PR. already adding it to avoid merge issues while completing the development of the PR.

throws MiIoCryptoException, IOException, JsonSyntaxException {
try {
JsonObject fullCommand = new JsonObject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,11 @@
"label": "Default"
},
{
"value": "1",
"value": "2",
"label": "CT mode"
},
{
"value": "2",
"value": "1",
"label": "RGB mode"
},
{
Expand All @@ -165,7 +165,7 @@
},
"refresh": true,
"actions": [],
"readmeComment": "Value mapping `[\"0\"\u003d\"Default\",\"1\"\u003d\"CT mode\",\"2\"\u003d\"RGB mode\",\"3\"\u003d\"HSV mode\",\"4\"\u003d\"Color Flow mode\",\"5\"\u003d\"Night Light mode\"]`"
"readmeComment": "Value mapping `[\"0\"\u003d\"Default\",\"2\"\u003d\"CT mode\",\"1\"\u003d\"RGB mode\",\"3\"\u003d\"HSV mode\",\"4\"\u003d\"Color Flow mode\",\"5\"\u003d\"Night Light mode\"]`"
},
{
"property": "name",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@
"label": "Default"
},
{
"value": "1",
"value": "2",
"label": "CT mode"
},
{
"value": "2",
"value": "1",
"label": "RGB mode"
},
{
Expand All @@ -140,7 +140,7 @@
},
"refresh": true,
"actions": [],
"readmeComment": "Value mapping `[\"0\"\u003d\"Default\",\"1\"\u003d\"CT mode\",\"2\"\u003d\"RGB mode\",\"3\"\u003d\"HSV mode\",\"4\"\u003d\"Color Flow mode\",\"5\"\u003d\"Night Light mode\"]`"
"readmeComment": "Value mapping `[\"0\"\u003d\"Default\",\"2\"\u003d\"CT mode\",\"1\"\u003d\"RGB mode\",\"3\"\u003d\"HSV mode\",\"4\"\u003d\"Color Flow mode\",\"5\"\u003d\"Night Light mode\"]`"
},
{
"property": "name",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,11 @@
"label": "Default"
},
{
"value": "1",
"value": "2",
"label": "CT mode"
},
{
"value": "2",
"value": "1",
"label": "RGB mode"
},
{
Expand All @@ -165,7 +165,7 @@
},
"refresh": true,
"actions": [],
"readmeComment": "Value mapping `[\"0\"\u003d\"Default\",\"1\"\u003d\"CT mode\",\"2\"\u003d\"RGB mode\",\"3\"\u003d\"HSV mode\",\"4\"\u003d\"Color Flow mode\",\"5\"\u003d\"Night Light mode\"]`"
"readmeComment": "Value mapping `[\"0\"\u003d\"Default\",\"2\"\u003d\"CT mode\",\"1\"\u003d\"RGB mode\",\"3\"\u003d\"HSV mode\",\"4\"\u003d\"Color Flow mode\",\"5\"\u003d\"Night Light mode\"]`"
},
{
"property": "name",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@
"label": "Default"
},
{
"value": "1",
"value": "2",
"label": "CT mode"
},
{
"value": "2",
"value": "1",
"label": "RGB mode"
},
{
Expand Down Expand Up @@ -166,14 +166,15 @@
"Control",
"Light"
],
"readmeComment": "Value mapping `[\"0\"\u003d\"Default\",\"1\"\u003d\"CT mode\",\"2\"\u003d\"RGB mode\",\"3\"\u003d\"HSV mode\",\"4\"\u003d\"Color Flow mode\",\"5\"\u003d\"Night Light mode\"]`"
"readmeComment": "Value mapping `[\"0\"\u003d\"Default\",\"2\"\u003d\"CT mode\",\"1\"\u003d\"RGB mode\",\"3\"\u003d\"HSV mode\",\"4\"\u003d\"Color Flow mode\",\"5\"\u003d\"Night Light mode\"]`"
},
{
"property": "rgb",
"friendlyName": "RGB Color",
"channel": "rgbColor",
"type": "Color",
"refresh": true,
"transformation": "addBrightToHSV",
"ChannelGroup": "actions",
"actions": [
{
Expand Down
Loading