Skip to content

Commit

Permalink
Fixed unit override via config
Browse files Browse the repository at this point in the history
  • Loading branch information
maxwroc committed Dec 29, 2023
1 parent 25c3f4a commit 3fb1cf6
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 42 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/continous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Build
run: npm run build
- name: Test
run: npm run test+coverage+all
run: npm run test+coverage
- name: Coveralls
uses: coverallsapp/[email protected]
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
run: npm run release

- name: Test
run: npm run test+coverage+all
run: npm run test+coverage

- name: Coveralls
uses: coverallsapp/[email protected]
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
"release": "rollup --environment RELEASE -c",
"watch": "rollup -c --watch",
"test": "jest",
"test+coverage": "jest --coverage --testPathPattern=test/other",
"test+coverage+all": "jest --coverage",
"test+integration": "jest --testPathPattern=test/entity",
"test+coverage": "jest --coverage",
"test+coverage+unit": "jest --coverage --testPathPattern=test/other",
"test+debug": "SET DEBUG_MODE=1&&jest"
},
"jest": {
Expand Down
12 changes: 3 additions & 9 deletions src/custom-elements/battery-state-entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,10 @@ export class BatteryStateEntity extends LovelaceCard<IBatteryEntityConfig> {
};

this.name = getName(this.config, this.hass);
var { state, level, unit_override} = getBatteryLevel(this.config, this.hass);
var { state, level, unit} = getBatteryLevel(this.config, this.hass);
this.state = state;

if (unit_override === undefined && level !== undefined && this.config.unit !== "" && this.config.unit !== null) {
this.unit = String.fromCharCode(160) + (this.config.unit || this.hass?.states[this.config.entity]?.attributes["unit_of_measurement"] || "%");
}
else {
this.unit = unit_override;
}

this.unit = unit;

const isCharging = getChargingState(this.config, this.state, this.hass);
this.secondaryInfo = getSecondaryInfo(this.config, this.hass, isCharging);
this.icon = getIcon(this.config, level, isCharging, this.hass);
Expand Down
6 changes: 4 additions & 2 deletions src/custom-elements/battery-state-entity.views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ ${icon(model.icon, model.iconColor)}
${secondaryInfo(model.secondaryInfo, model.hass)}
</div>
<div class="state">
${model.state}${model.unit}
${model.state}${unit(model.unit)}
</div>
`;
`;

const unit = (unit: string | undefined) => unit && html`&nbsp;${unit}`;
27 changes: 23 additions & 4 deletions src/entity-fields/battery-level.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export const getBatteryLevel = (config: IBatteryEntityConfig, hass?: HomeAssista
const processedValue = stringProcessor.process(config.value_override.toString());
return {
state: processedValue,
level: isNumber(processedValue) ? Number(processedValue) : undefined
level: isNumber(processedValue) ? Number(processedValue) : undefined,
unit: getUnit(processedValue, undefined, config, hass),
}
}

Expand Down Expand Up @@ -101,16 +102,34 @@ export const getBatteryLevel = (config: IBatteryEntityConfig, hass?: HomeAssista

// assuming it is a number followed by unit
[displayValue, unit] = formattedState.split(" ", 2);
unit = String.fromCharCode(160) + unit;
unit = unit;
}

return {
state: displayValue || state,
level: isNumber(state) ? Number(state) : undefined,
unit_override: unit,
unit: getUnit(state, unit, config, hass),
};
}

const getUnit = (state: string, unit: string | undefined, config: IBatteryEntityConfig, hass?: HomeAssistantExt): string | undefined => {
if (config.unit) {
// config unit override
unit = config.unit
}
else {
// default unit
unit = unit || hass?.states[config.entity]?.attributes["unit_of_measurement"] || "%"
}

if (!isNumber(state)) {
// for non numeric states unit should not be rendered
unit = undefined;
}

return unit;
}

interface IBatteryState {
/**
* Battery level
Expand All @@ -125,5 +144,5 @@ interface IBatteryState {
/**
* Unit override
*/
unit_override?: string
unit?: string
}
80 changes: 57 additions & 23 deletions test/other/entity-fields/battery-level.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,54 @@ describe("Battery level", () => {

test("is equal value_override setting when it is provided", () => {
const hassMock = new HomeAssistantMock(true);
const { state, level } = getBatteryLevel({ entity: "any", value_override: "45" }, hassMock.hass);
const { state, level, unit } = getBatteryLevel({ entity: "any", value_override: "45" }, hassMock.hass);

expect(level).toBe(45);
expect(state).toBe("45");
expect(unit).toBe("%");
});

test("is 'Unknown' when entity not found and no localized string", () => {
const hassMock = new HomeAssistantMock(true);
hassMock.hass.localize = () => <string><unknown>null;
const { state, level } = getBatteryLevel({ entity: "any" }, hassMock.hass);
const { state, level, unit } = getBatteryLevel({ entity: "any" }, hassMock.hass);

expect(level).toBeUndefined()
expect(level).toBeUndefined();
expect(state).toBe("Unknown");
expect(unit).toBeUndefined();
});

test("is 'Unknown' localized string when entity not found", () => {
const hassMock = new HomeAssistantMock(true);
const { state, level } = getBatteryLevel({ entity: "any" }, hassMock.hass);
const { state, level, unit } = getBatteryLevel({ entity: "any" }, hassMock.hass);

expect(level).toBeUndefined()
expect(level).toBeUndefined();
expect(state).toBe("[state.default.unknown]");
expect(unit).toBeUndefined();
});

test("is taken from attribute but attribute is missing", () => {

const hassMock = new HomeAssistantMock(true);
hassMock.addEntity("Mocked entity", "OK", { battery_state: "45" });

const { state, level } = getBatteryLevel({ entity: "mocked_entity", attribute: "battery_state_missing" }, hassMock.hass);
const { state, level, unit } = getBatteryLevel({ entity: "mocked_entity", attribute: "battery_state_missing" }, hassMock.hass);

expect(level).toBeUndefined()
expect(level).toBeUndefined();
expect(state).toBe("[state.default.unknown]");
expect(unit).toBeUndefined();
});

test("is taken from attribute", () => {

const hassMock = new HomeAssistantMock(true);
hassMock.addEntity("Mocked entity", "OK", { battery_state: "45" });

const { state, level } = getBatteryLevel({ entity: "mocked_entity", attribute: "battery_state" }, hassMock.hass);
const { state, level, unit } = getBatteryLevel({ entity: "mocked_entity", attribute: "battery_state" }, hassMock.hass);

expect(level).toBe(45);
expect(state).toBe("45");
expect(unit).toBe("%");
});

test("is taken from attribute - value includes percentage", () => {
Expand Down Expand Up @@ -164,41 +169,70 @@ describe("Battery level", () => {
});

test.each([
["ok", "100", 100, undefined],
["empty", "0", 0, undefined],
["20", "20", 20, undefined],
["charge", "Empty", 0, "Empty"],
["charge", "StateFromOtherEntity", 0, "{sensor.other_entity.state}"],
["ok", "100", 100, "%", undefined],
["empty", "0", 0, "%", undefined],
["20", "20", 20, "%", undefined],
["charge", "Empty", 0, "%", "Empty"],
["charge", "StateFromOtherEntity", 0, "%", "{sensor.other_entity.state}"],
])
("state map applied", (entityState: string, expectedState: string, expectedLevel: number | undefined, display?: string) => {
("state map applied", (entityState: string, expectedState: string, expectedLevel: number | undefined, expectedUnit: string | undefined, display?: string) => {

const hassMock = new HomeAssistantMock(true);
hassMock.addEntity("Mocked entity", entityState);
hassMock.addEntity("Other entity", "StateFromOtherEntity", undefined, "sensor");

const { state, level } = getBatteryLevel({ entity: "mocked_entity", state_map: [ { from: "ok", to: "100" }, { from: "empty", to: "0" }, { from: "charge", to: "0", display } ] }, hassMock.hass);
const { state, level, unit } = getBatteryLevel({ entity: "mocked_entity", state_map: [ { from: "ok", to: "100" }, { from: "empty", to: "0" }, { from: "charge", to: "0", display } ] }, hassMock.hass);

expect(level).toBe(expectedLevel);
expect(state).toBe(expectedState);
expect(unit).toBe(expectedUnit);
});

test.each([
[undefined, "45", "dbm", { state: "[45]", level: 45, unit_override: String.fromCharCode(160) + "[dbm]" }], // test default when the setting is not set in the config
[true, "45", "dbm", { state: "[45]", level: 45, unit_override: String.fromCharCode(160) + "[dbm]" }], // test when the setting is explicitly true
[false, "45", "dbm", { state: "45", level: 45, unit_override: undefined }], // test when the setting is turned off
[true, "45", "dbm", { state: "56", level: 56, unit_override: undefined }, [ { from: "45", to: "56" } ]], // test when the state was changed by state_map
[true, "45", "dbm", { state: "33", level: 45, unit_override: undefined }, [ { from: "45", to: "45", display: "33" } ]], // test when the display value was changed by state_map
[undefined, "45", "dbm", { state: "[45]", level: 45, unit: "[dbm]" }], // test default when the setting is not set in the config
[true, "45", "dbm", { state: "[45]", level: 45, unit: "[dbm]" }], // test when the setting is explicitly true
[false, "45", "dbm", { state: "45", level: 45, unit: "%" }], // test when the setting is turned off
[true, "45", "dbm", { state: "56", level: 56, unit: "%" }, [ { from: "45", to: "56" } ]], // test when the state was changed by state_map
[true, "45", "dbm", { state: "33", level: 45, unit: "%" }, [ { from: "45", to: "45", display: "33" } ]], // test when the display value was changed by state_map
])
("default HA formatting ", (defaultStateFormatting: boolean | undefined, entityState: string, unitOfMeasurement: string, expected: { state: string, level: number, unit_override?: string }, stateMap: IConvert[] | undefined = undefined) => {
("default HA formatting ", (defaultStateFormatting: boolean | undefined, entityState: string, unitOfMeasurement: string, expected: { state: string, level: number, unit?: string }, stateMap: IConvert[] | undefined = undefined) => {

const hassMock = new HomeAssistantMock(true);
hassMock.addEntity("Mocked entity", entityState);
hassMock.mockFunc("formatEntityState", (entityData: any) => `[${entityData.state}] [${unitOfMeasurement}]`);

const { state, level, unit_override } = getBatteryLevel({ entity: "mocked_entity", default_state_formatting: defaultStateFormatting, state_map: stateMap }, hassMock.hass);
const { state, level, unit } = getBatteryLevel({ entity: "mocked_entity", default_state_formatting: defaultStateFormatting, state_map: stateMap }, hassMock.hass);

expect(level).toBe(expected.level);
expect(state).toBe(expected.state);
expect(unit_override).toBe(expected.unit_override);
expect(unit).toBe(expected.unit);
});

test.each([
["OK", undefined, undefined, undefined],
["45", undefined, undefined, "%"],
["45", "dBm", undefined, "dBm"],
["45", "dBm", "rpm", "rpm"],
])("unit is correct", (entityState: string, entityUnitOfMeasurement: string | undefined, configOverride: string | undefined, expectedUnit: string | undefined) => {
const hassMock = new HomeAssistantMock(true);
const entity = hassMock.addEntity("Mocked entity", entityState, { unit_of_measurement: entityUnitOfMeasurement });

const { unit } = getBatteryLevel({ entity: entity.entity_id, default_state_formatting: false, unit: configOverride }, hassMock.hass);

expect(unit).toBe(expectedUnit);
})

test.each([
["OK", undefined, undefined, undefined],
["45", undefined, undefined, "%"],
["45", "dBm", undefined, "dBm"],
["45", "dBm", "rpm", "rpm"],
])("unit is correct when value_override is used", (entityState: string, entityUnitOfMeasurement: string | undefined, configOverride: string | undefined, expectedUnit: string | undefined) => {
const hassMock = new HomeAssistantMock(true);
const entity = hassMock.addEntity("Mocked entity", entityState, { unit_of_measurement: entityUnitOfMeasurement });

const { unit } = getBatteryLevel({ entity: entity.entity_id, default_state_formatting: false, unit: configOverride, value_override: "{state}" }, hassMock.hass);

expect(unit).toBe(expectedUnit);
})
});

0 comments on commit 3fb1cf6

Please sign in to comment.