Playing around with the awesome new Lovelace UI, I created a custom card to show live departure times for public transport in Munich, using the MVG Live sensor. (To make this possible I also had to modify the sensor and will submit the modifications as a PR soon.)
You could release a custom component of that patch until it’s out. Also some css variables would be nice if you want to allow customisation of the card in the future and to match theme of user
Also could create dom in setConfig and use set (hass) only for updates if state is different.
This card works very well, but I found a little bug. When the MVG Live sensor does not return any connection, there is a lovelace exception thrown in my UI:
https://.../local/mvg-card.js:108:46 Uncaught TypeError: state.attributes.departures is not iterable
Hi could you help me out?
I put your js in config/www/custom_lovelace/mvg_card.js
Added it as a resource in Lovelace Settings as /local/mvg_card.js (and also /local/custom_lovelace/mvg_card.js because i was not sure)
And tried to add it to Lovelace with - type: 'custom:mvg-card'
But i only get:
Hey there!
Joined HomeAssistant Fam 2 days ago and was really interested to use this, but didn’t get it to work. I’ve tried a lot and in the end, it worked for me.
Idk how and why, I’m just grateful it works.
Notes:
In your lovelace dashboard, put the card into a conditional card, like this:
Because if the next departure is more than 2h away, the sensor’s state goes to ‘-’, And the card wouldn’t display anything
The sensor’s direction attribute doesn’t seem to be supported by mvg anymore, at least for mine it always retrieved an empty field for each departure. If you’re not getting any data, delete that condition out of your config file
And this is the code. Call the js file new-mvg-card.js and it should work without problems (hopefully)
class NewMvgCard extends HTMLElement {
// Whenever the state changes, a new `hass` object is set. Use this to
// update your content.
set hass(hass) {
const entityId = this.config.entity;
const state = hass.states[entityId];
const stateStr = state ? state.state : 'unavailable';
const name = state.attributes['friendly_name'];
if (!this.content) {
const card = document.createElement('ha-card');
card.header = name;
this.content = document.createElement('div');
const style = document.createElement('style');
style.textContent = `
table {
width: 100%;
padding: 6px 14px;
}
td {
padding: 3px 0px;
}
td.shrink {
white-space:nowrap;
}
td.expand {
width: 99%
}
span.line {
font-weight: bold;
font-size:0.9em;
padding:3px 8px 2px 8px;
color: #fff;
background-color: #888;
margin-right:0.7em;
}
span.S-Bahn {
border-radius:999px;
}
span.U-Bahn {
}
span.Tram {
background-color: #ee1c25;
}
span.Bus {
background-color: #005f5f;
}
span.U1 {
background-color: #3c7233;
}
span.U2 {
background-color: #c4022e;
}
span.U3 {
background-color: #ed6720;
}
span.U4 {
background-color: #00ab85;
}
span.U5 {
background-color: #be7b00;
}
span.U6 {
background-color: #0065af;
}
span.U7 {
background-color: #c4022e;
}
span.U8 {
background-color: #ed6720;
}
span.S1 {
background-color: #16c0e9;
}
span.S2 {
background-color: #71bf44;
}
span.S3 {
background-color: #7b107d;
}
span.S4 {
background-color: #ee1c25;
}
span.S5 {
background-color: #ffcc00;
color: black;
}
span.S6 {
background-color: #008a51;
}
span.S7 {
background-color: #963833;
}
span.S8 {
background-color: black;
color: #ffcb06;
}
span.S20 {
background-color: #f05a73;
}
`
card.appendChild(style);
card.appendChild(this.content);
this.appendChild(card);
}
var tablehtml = `
<table>
`
if(state != '-'){
for (const attributes of state.attributes['departures']) {
const icon = attributes['icon']
const destination = attributes['destination']
const linename = attributes['linename']
const product = attributes['product']
const time = attributes['time']
const iconel = document.createElement('ha-icon');
iconel.setAttribute('icon', icon);
const iconHTML = iconel.outerHTML;
tablehtml += `
<tr>
<td class="shrink" style="text-align:center;"><span class="line ${product} ${linename}">${linename}</span></td>
<td class="expand">${destination}</td>
<td class="shrink" style="text-align:right;">${time}</td>
</tr>
`;
}
}
tablehtml += `
</table>
`
this.content.innerHTML = tablehtml
}
// The user supplied configuration. Throw an exception and Lovelace will
// render an error card.
setConfig(config) {
if (!config.entity) {
throw new Error('You need to define an entity');
}
this.config = config;
}
// The height of your card. Home Assistant uses this to automatically
// distribute all cards over the available columns.
getCardSize() {
return 1;
}
}
customElements.define('new-mvg-card', NewMvgCard);
// Configure the preview in the Lovelace card picker
window.customCards = window.customCards || [];
window.customCards.push({
type: 'new-mvg-card',
name: 'MVG Card',
preview: false,
description: 'This card displays departures for a sensor created with mvglive',
});
After adding, you should be able to add the card with the graphical interface. From there you only have to add your entity name. In total it should look like this:
yep, it stopped working with the last change of the timetable.
I’m currently testing the HAFAS integration https://github.com/akloeckner/hacs-hafas. It works for the S Bahn, I’ve not tried any other transportations, and gives the next three connections plus delay of the first one.
Since I recently moved to Munich, I started looking for an MVG integration. There are currently several integrations with different versions circulating through the forum:
Unfortunately, there are virtually no detailed tutorials for creating custom cards. There are still a few issues in mine (e.g. working preview via setConfig()). I would be very happy if someone here could find good documentation or tutorials for this. The whole topic is only touched on very superficially on the developer pages.
Well, it works well at the moment. Maybe there is someone else here who can improve it a bit