Skip to content

Commit

Permalink
KString new functions #490
Browse files Browse the repository at this point in the history
  • Loading branch information
maxwroc committed Oct 21, 2023
1 parent 05813e6 commit 4a65588
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 1 deletion.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ This card was inspired by [another great card](https://github.com/cbulock/lovela
| icon | string | | v1.6.0 | Icon override (if you want to set a static custom one). You can provide entity attribute name which contains icon class (e.g. `attributes.battery_icon` - it has to be prefixed with "attributes.")
| attribute | string | | v0.9.0 | Name of attribute (override) to extract the value from. By default we look for values in the following attributes: `battery_level`, `battery`. If they are not present we take entity state.
| multiplier | number | `1` | v0.9.0 | If the value is not in 0-100 range we can adjust it by specifying multiplier. E.g. if the values are in 0-10 range you can make them working by putting `10` as multiplier.
| value_override | [KString](#keyword-string-kstring) | | v3.0.0 | Allows to override the battery level value. Note: when used the `multiplier`, `round`, `state_map` setting is ignored

+[common options](#common-options) (if specified they will override the card-level ones)

Expand Down Expand Up @@ -90,8 +91,13 @@ Keywords support simple functions to convert the values
|:-----|:-----|:-----|
| round(\[number\]) | `"{state\|round(2)}"` | Rounds the value to number of fractional digits. E.g. if state is 20.617 the output will be 20.62.
| replace(\[old_string\]=\[new_string\]) | `"{attributes.friendly_name\|replace(Battery level=)}"` | Simple replace. E.g. if name contains "Battery level" string then it will be removed
| multiply(\[number\]) | `"{state\|multiply(10)}"` | Multiplies the value by given number
| greaterthan(\[threshold_number\],\[result_value\]) | `"{state\|greaterthan(10,100)}"` | Changes the value to a given one when the threshold is met. In the given example the value will be replaced to 100 when the current value is greater than 10
| lessthan(\[threshold_number\],\[result_value\]) | `"{state\|lessthan(10,0)}"` | Changes the value to a given one when the threshold is met. In the given example the value will be replaced to 0 when the current value is less than 10
| between(\[lower_threshold_number\],[upper_threshold_number\],\[result_value\]) | `"{state\|between(2,6,30)}"` | Changes the value to a given one when the value is between two given numbers. In the given example the value will be replaced to 30 when the current value is between 2 and 6
| thresholds(\[number1\],\[number2\],...) | `"{state\|thresholds(22,89,200,450)}"` | Converts the value to percentage based on given thresholds. In the given example values will be converted in the following way 20=>0, 30=>25, 99=>50, 250=>75, 555=>100

You can execute functions one after another. For example if you have the value "Battery level: 26.543234%" and you want to extract and round the number then you can do the following: `"{attribute.battery_level|replace(Battery level:=)|replace(%=)|round()} %"` and the end result will be "27 %"
You can execute functions one after another. For example if you have the value "Battery level: 26.543234%" and you want to extract and round the number then you can do the following: `"{attribute.battery_level|replace(Battery level:=)|replace(%=)|round()} %"` and the end result will be "27"

### Sort object

Expand Down
59 changes: 59 additions & 0 deletions src/rich-string-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,65 @@ const availableProcessors: IMap<IProcessorCtor> = {
}

return val => parseFloat(val).toFixed(decimalPlaces);
},
"multiply": (params) => {
if (params === "") {
log("[KString]multiply function is missing parameter");
return val => val;
}

const multiplier = Number(params);

return val => isNaN(multiplier) ? val : (Number(val) * multiplier).toString();
},
"greaterthan": (params) => {
const chunks = params.split(",");
if (chunks.length != 2) {
log("[KString]greaterthan function requires two parameters");
return val => val;
}

const compareTo = Number(chunks[0]);
return val => Number(val) > compareTo ? chunks[1] : val;
},
"lessthan": (params) => {
const chunks = params.split(",");
if (chunks.length != 2) {
log("[KString]lessthan function requires two parameters");
return val => val;
}

const compareTo = Number(chunks[0]);
return val => Number(val) < compareTo ? chunks[1] : val;
},
"between": (params) => {
const chunks = params.split(",");
if (chunks.length != 3) {
log("[KString]between function requires three parameters");
return val => val;
}

const compareLower = Number(chunks[0]);
const compareGreater = Number(chunks[1]);
return val => {
const numericVal = Number(val);
return compareLower < numericVal && compareGreater > numericVal ? chunks[2] : val;
}
},
"thresholds": (params) => {
const thresholds = params.split(",").map(v => Number(v));

return val => {
const numericVal = Number(val);
const result = thresholds.findIndex(v => numericVal < v);

if (result == -1) {
// looks like the value is higher than the last threshold
return "100";
}

return Math.round(100 / thresholds.length * result).toString();
}
}
}

Expand Down
52 changes: 52 additions & 0 deletions test/other/rich-string-processor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,56 @@ describe("RichStringProcessor", () => {
const result = proc.process("{is_charging}");
expect(result).toBe("Charging");
});

test.each([
["Value {state|multiply(2)}", "20.56", "Value 41.12"],
["Value {state|multiply(0.5)}", "20.56", "Value 10.28"],
["Value {state|multiply()}", "20.56", "Value 20.56"], // param missing
])("multiply function", (text: string, state:string, expectedResult: string) => {
const hassMock = new HomeAssistantMock<BatteryStateEntity>(true);
const motionEntity = hassMock.addEntity("Bedroom motion", state, {}, "sensor");
const proc = new RichStringProcessor(hassMock.hass, motionEntity.entity_id);

const result = proc.process(text);
expect(result).toBe(expectedResult);
});

test.each([
["{state|lessthan(2,0)|greaterthan(7,100)|between(1,8,50)}", "1", "0"],
["{state|lessthan(2,0)|greaterthan(7,100)|between(1,8,50)}", "2", "50"],
["{state|lessthan(2,0)|greaterthan(7,100)|between(1,8,50)}", "5", "50"],
["{state|lessthan(2,0)|greaterthan(7,100)|between(1,8,50)}", "7", "50"],
["{state|lessthan(2,0)|greaterthan(7,100)|between(1,8,50)}", "8", "100"],
["{state|lessthan(2,0)|greaterthan(7,100)|between(1,8,50)}", "70", "100"],
// missing params
["{state|lessthan()|greaterthan(7,100)|between(1,8,50)}", "1", "1"],
["{state|lessthan(2,0)|greaterthan(7,100)|between()}", "5", "5"],
["{state|lessthan(2,0)|greaterthan()|between(1,8,50)}", "70", "70"],
])("greater, lessthan, between functions", (text: string, state:string, expectedResult: string) => {
const hassMock = new HomeAssistantMock<BatteryStateEntity>(true);
const motionEntity = hassMock.addEntity("Bedroom motion", state, {}, "sensor");
const proc = new RichStringProcessor(hassMock.hass, motionEntity.entity_id);

const result = proc.process(text);
expect(result).toBe(expectedResult);
});

test.each([
["{state|thresholds(22,88,200,450)}", "1", "0"],
["{state|thresholds(22,88,200,450)}", "22", "25"],
["{state|thresholds(22,88,200,450)}", "60", "25"],
["{state|thresholds(22,88,200,450)}", "90", "50"],
["{state|thresholds(22,88,200,450)}", "205", "75"],
["{state|thresholds(22,88,200,450)}", "449", "75"],
["{state|thresholds(22,88,200,450)}", "500", "100"],
["{state|thresholds(22,88,200)}", "90", "67"],
["{state|thresholds(22,88,200)}", "200", "100"],
])("threshold function", (text: string, state:string, expectedResult: string) => {
const hassMock = new HomeAssistantMock<BatteryStateEntity>(true);
const motionEntity = hassMock.addEntity("Bedroom motion", state, {}, "sensor");
const proc = new RichStringProcessor(hassMock.hass, motionEntity.entity_id);

const result = proc.process(text);
expect(result).toBe(expectedResult);
});
})

0 comments on commit 4a65588

Please sign in to comment.