Skip to content

Commit

Permalink
Improved Valetudo 2022.01.0 support
Browse files Browse the repository at this point in the history
  • Loading branch information
TheLastProject committed Dec 31, 2021
1 parent 4065f4b commit f887a55
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 41 deletions.
23 changes: 8 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ Draws the map from a Xiaomi vacuum cleaner, that is rooted and flashed with [Val
## Valetudo
Valetudo can be found on https://github.com/Hypfer/Valetudo. This is the only version of Valetudo we officially support.

This card requires at least Valetudo 2021.2.0.
This card requires at least Valetudo 2022.01.0.

If you want to use an older Valetudo version, check out the legacy branch of this project. In HACS, you can choose "Reinstall" and use version v2020.
If you want to use an older Valetudo version, check out the legacy or 2021.2.0 branch of this project. In HACS, you can choose "Reinstall" and use version v2020 or 2021-12-05.

## Install

Expand All @@ -30,33 +30,26 @@ resources:
`configuration.yaml`: Valetudo officially supports MQTT, with the preferred example configuration as follows. You will need to have MQTT configured in [Home Assistant](https://www.home-assistant.io/docs/mqtt/broker) and [Valetudo](https://hypfer.github.io/Valetudo/pages/integrations/home-assistant-integration.html).

When using the latest (2021.2.0++) version of Valetudo, you should see a new `camera.map_data` (or `camera.map` before 2021.4.0) entity which actually contains the map data.
Simply configure this card to use it and everything should be fine :)
A new MQTT entity named something like `vacuum.valetudo_fairyouthfulmandrill` should appear.

### Lovelace custom card

Even when installing via HACS, the new card will **not** appear automatically in the list of card previews when hitting the "+" button on the UI. Instead, choose the "Manual" option and provide the following yaml content:

```yaml
type: 'custom:valetudo-map-card'
entity: camera.map_data
vacuum_entity: vacuum.valetudo_robot
rotate: 0
crop:
top: 0
bottom: 0
left: 0
right: 0
min_height: 0
vacuum: 'valetudo_fairyouthfulmandrill'
```

The name of vacuum is based on the MQTT entity, with the `vacuum.` prefix removed. It will probably have another name for you, but will likely start with `valetudo_`.

## Options
| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| type | string | **Required** | `custom:valetudo-map-card`
| vacuum | string | **Required** | Name of the vacuum in MQTT (without vacuum. prefix)
| title | string | Vacuum | Title to show in the card header
| entity | string | | Camera entity to get state from
| vacuum_entity | string | | Vacuum to show buttons to control for
| show_map | boolean | true | Show the map
| background_color | string | | Background color of the card
| floor_color | string | '--valetudo-map-floor-color', '--secondary-background-color' | Floor color
| floor_opacity | number | 1 | Floor opacity
Expand Down
70 changes: 44 additions & 26 deletions valetudo-map-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@ class ValetudoMapCard extends HTMLElement {
this.cardContainer.appendChild(this.controlContainerStyle);
};

getMapEntityName(vacuum_name) {
return "camera." + vacuum_name + "_map_data";
}

getVacuumEntityName(vacuum_name) {
return "vacuum." + vacuum_name;
}

getMapEntity(vacuum_name) {
return this._hass.states[this.getMapEntityName(vacuum_name)];
}

getVacuumEntity(vacuum_name) {
return this._hass.states[this.getVacuumEntityName(vacuum_name)];
}

shouldDrawMap() {
return !this.drawingMap;
};
Expand Down Expand Up @@ -526,7 +542,10 @@ class ValetudoMapCard extends HTMLElement {
// Title settings
if (this._config.title === undefined) this._config.title = "Vacuum";

// Show settings
// Core show settings
if (this._config.show_map === undefined) this._config.show_map = true;

// Map show settings
if (this._config.show_floor === undefined) this._config.show_floor = true;
if (this._config.show_dock === undefined) this._config.show_dock = true;
if (this._config.show_vacuum === undefined) this._config.show_vacuum = true;
Expand All @@ -542,6 +561,8 @@ class ValetudoMapCard extends HTMLElement {
if (this._config.show_predicted_path === undefined) this._config.show_predicted_path = true;
if (this._config.show_goto_target === undefined) this._config.show_goto_target = true;
if (this._config.show_segments === undefined) this._config.show_segments = true;

// Info show settings
if (this._config.show_status === undefined) this._config.show_status = true;
if (this._config.show_battery_level === undefined) this._config.show_battery_level = true;

Expand Down Expand Up @@ -619,24 +640,18 @@ class ValetudoMapCard extends HTMLElement {

this._hass = hass;

let mapEntity = this._hass.states[this._config.entity];
let vacuumEntity;
let mapEntity = this.getMapEntity(this._config.vacuum);
let vacuumEntity = this.getVacuumEntity(this._config.vacuum);
let shouldForcePoll = false;

let attributes = mapEntity ? mapEntity.attributes : undefined;

if(this._config.vacuum_entity && this._hass.states[this._config.vacuum_entity]) {
vacuumEntity = this._hass.states[this._config.vacuum_entity];


if (vacuumEntity.state !== this.lastRobotState) {
this.pollInterval = POLL_INTERVAL_STATE_MAP[vacuumEntity.state] || 10000;
shouldForcePoll = true;
this.lastRobotState = vacuumEntity.state;
}
if (vacuumEntity && vacuumEntity.state !== this.lastRobotState) {
this.pollInterval = POLL_INTERVAL_STATE_MAP[vacuumEntity.state] || 10000;
shouldForcePoll = true;
this.lastRobotState = vacuumEntity.state;
}


if (mapEntity && mapEntity['state'] !== 'unavailable' && attributes && attributes["entity_picture"]) {
if (new Date().getTime() - this.pollInterval > this.lastMapPoll.getTime() || shouldForcePoll) {
this.loadImageAndExtractMapData(attributes["entity_picture"]).then(mapData => {
Expand Down Expand Up @@ -685,7 +700,7 @@ class ValetudoMapCard extends HTMLElement {

handleDrawing(hass, mapEntity, attributes) {
const config = this._config;
let infoEntity = this._hass.states[this._config.vacuum_entity]
let infoEntity = this.getVacuumEntity(this._config.vacuum);

let canDrawMap = false;
let canDrawControls = true;
Expand Down Expand Up @@ -719,24 +734,27 @@ class ValetudoMapCard extends HTMLElement {
this.lastUpdatedControls = ""
}

if (!canDrawMap && this._config.entity) {
if (!canDrawMap || !this._config.show_map) {
// Remove the map
this.mapContainer.style.display = 'none';
} else {
this.mapContainer.style.display = 'block';
}

if (!canDrawMap && this._config.show_map) {
// Show the warning
this.entityWarning1.textContent = `Entity not available: ${this._config.entity}`;
this.entityWarning1.textContent = `Entity not available: ${this.getMapEntityName(this._config.vacuum)}`;
this.entityWarning1.style.display = 'block';
} else {
this.entityWarning1.style.display = 'none';
this.mapContainer.style.display = 'block';
}

if (!canDrawControls && this._config.vacuum_entity) {
if (!canDrawControls) {
// Remove the controls
this.controlContainer.style.display = 'none';

// Show the warning
this.entityWarning2.textContent = `Entity not available: ${this._config.vacuum_entity}`;
this.entityWarning2.textContent = `Entity not available: ${this.getVacuumEntityName(this._config.vacuum)}`;
this.entityWarning2.style.display = 'block';
} else {
this.entityWarning2.style.display = 'none';
Expand Down Expand Up @@ -826,7 +844,7 @@ class ValetudoMapCard extends HTMLElement {
const vacuumColor = this.calculateColor(homeAssistant, this._config.vacuum_color, '--primary-text-color');
const gotoTargetColor = this.calculateColor(homeAssistant, this._config.goto_target_color, 'blue');

if (this.shouldDrawMap()) {
if (this.shouldDrawMap() && this._config.show_map) {
// Start drawing map
this.drawingMap = true;

Expand Down Expand Up @@ -857,7 +875,7 @@ class ValetudoMapCard extends HTMLElement {
}
`

let infoEntity = this._hass.states[this._config.vacuum_entity]
let infoEntity = this.getVacuumEntity(this._config.vacuum);
if (this.shouldDrawControls(infoEntity)) {
// Start drawing controls
this.drawingControls = true;
Expand Down Expand Up @@ -909,7 +927,7 @@ class ValetudoMapCard extends HTMLElement {
startButton.appendChild(startIcon);
startButton.appendChild(startRipple);
startButton.addEventListener('click', (event) => {
this._hass.callService('vacuum', 'start', { entity_id: this._config.vacuum_entity }).then();
this._hass.callService('vacuum', 'start', { entity_id: this.getVacuumEntityName(this._config.vacuum) }).then();
});
this.controlFlexBox.appendChild(startButton);
}
Expand All @@ -922,7 +940,7 @@ class ValetudoMapCard extends HTMLElement {
pauseButton.appendChild(pauseIcon);
pauseButton.appendChild(pauseRipple);
pauseButton.addEventListener('click', (event) => {
this._hass.callService('vacuum', 'pause', { entity_id: this._config.vacuum_entity }).then();
this._hass.callService('vacuum', 'pause', { entity_id: this.getVacuumEntityName(this._config.vacuum) }).then();
});
this.controlFlexBox.appendChild(pauseButton);
}
Expand All @@ -935,7 +953,7 @@ class ValetudoMapCard extends HTMLElement {
stopButton.appendChild(stopIcon);
stopButton.appendChild(stopRipple);
stopButton.addEventListener('click', (event) => {
this._hass.callService('vacuum', 'stop', { entity_id: this._config.vacuum_entity }).then();
this._hass.callService('vacuum', 'stop', { entity_id: this.getVacuumEntityName(this._config.vacuum) }).then();
});
this.controlFlexBox.appendChild(stopButton);
}
Expand All @@ -948,7 +966,7 @@ class ValetudoMapCard extends HTMLElement {
homeButton.appendChild(homeIcon);
homeButton.appendChild(homeRipple);
homeButton.addEventListener('click', (event) => {
this._hass.callService('vacuum', 'return_to_base', { entity_id: this._config.vacuum_entity }).then();
this._hass.callService('vacuum', 'return_to_base', { entity_id: this.getVacuumEntityName(this._config.vacuum) }).then();
});
this.controlFlexBox.appendChild(homeButton);
}
Expand All @@ -961,7 +979,7 @@ class ValetudoMapCard extends HTMLElement {
locateButton.appendChild(locateIcon);
locateButton.appendChild(locateRipple);
locateButton.addEventListener('click', (event) => {
this._hass.callService('vacuum', 'locate', { entity_id: this._config.vacuum_entity }).then();
this._hass.callService('vacuum', 'locate', { entity_id: this.getVacuumEntityName(this._config.vacuum) }).then();
});
this.controlFlexBox.appendChild(locateButton);
}
Expand Down

0 comments on commit f887a55

Please sign in to comment.