I’m really particular/ocd/anal (whatever you want to call it) about about being able to control every aspect of a card style.
And sometimes, not even Card Mod can help, or I don’t feel like spending any more time trying to make it work.
I didn’t love any of the existing Weather Forecast cards out there, so I made my own only using Button Cards.
Anyone have any thoughts or feedback on what I could do to improve it?
I do have a separate card for current weather. This one is just for the 5 day forecast.
EDIT: Below is the code if anyone wants to use it. Sorry it’s not very user friendly.
It uses:
- Button Card (does most of the work)
- Decluttering Card (although it doesn’t have to)
- Card Mod (although could do this all with inline CSS)
- Horizontal Stack (1 column per day)
The [[[forecast_index]]] code has to be replaced with whatever code/value your weather integration uses to denote the day/date of the week.
Then use a horizontal stack card with 5 columns, with this code in each column, modifying the [[[forecast_index]]] code in each column as mentioned above.
type: custom:button-card
layout: vertical
show_icon: false
show_state: false
show_name: false
custom_fields:
day: |
[[[
const forecastEntity = states['sensor.pirate_weather_daily'];
if (forecastEntity && forecastEntity.attributes && forecastEntity.attributes.forecast) {
const forecastDate = new Date(forecastEntity.attributes.forecast[[[forecast_index]]].datetime);
const options = { weekday: 'short' };
return forecastDate.toLocaleDateString(undefined, options);
}
return 'N/A';
]]]
icon: |
[[[
const forecastEntity = states['sensor.pirate_weather_daily'];
if (forecastEntity && forecastEntity.attributes && forecastEntity.attributes.forecast) {
const forecast = forecastEntity.attributes.forecast[[[forecast_index]]];
const condition = forecast.condition.toLowerCase();
const iconMapping = {
'sunny': 'mdi:white-balance-sunny',
'cloudy': 'mdi:weather-cloudy',
'rainy': 'mdi:weather-rainy',
// Add more mappings as needed
};
return `<ha-icon icon="${iconMapping[condition] || 'mdi:weather-partly-cloudy'}"></ha-icon>`;
}
return 'N/A';
]]]
condition: |
[[[
const forecastEntity = states['sensor.pirate_weather_daily'];
return forecastEntity && forecastEntity.attributes && forecastEntity.attributes.forecast
? `${forecastEntity.attributes.forecast[[[forecast_index]]].condition}`
: 'N/A';
]]]
high_temp: |
[[[
const forecastEntity = states['sensor.pirate_weather_daily'];
if (forecastEntity && forecastEntity.attributes && forecastEntity.attributes.forecast) {
const high = forecastEntity.attributes.forecast[[[forecast_index]]].temperature;
return `<div>${high}<span class="symbol">°F</span></div><div class="label-large">High Temp</div>`;
}
return 'N/A';
]]]
low_temp: |
[[[
const forecastEntity = states['sensor.pirate_weather_daily'];
if (forecastEntity && forecastEntity.attributes && forecastEntity.attributes.forecast) {
const low = forecastEntity.attributes.forecast[[[forecast_index]]].templow;
return `<div>${low}<span class="symbol">°F</span></div><div class="label-large">Low Temp</div>`;
}
return 'N/A';
]]]
humidity: |
[[[
const forecastEntity = states['sensor.pirate_weather_daily'];
return forecastEntity && forecastEntity.attributes && forecastEntity.attributes.forecast
? `<div class="label-small">Humidity</div><div>${forecastEntity.attributes.forecast[[[forecast_index]]].humidity}%</div>`
: 'N/A';
]]]
precipitation: |
[[[
const forecastEntity = states['sensor.pirate_weather_daily'];
return forecastEntity && forecastEntity.attributes && forecastEntity.attributes.forecast
? `</div><div class="label-small">Rain</div><div>${forecastEntity.attributes.forecast[[[forecast_index]]].precipitation}"`
: 'N/A';
]]]
precipitation_probability: |
[[[
const forecastEntity = states['sensor.pirate_weather_daily'];
return forecastEntity && forecastEntity.attributes && forecastEntity.attributes.forecast
? `<div class="label-small">Rain Chance</div><div>${forecastEntity.attributes.forecast[[[forecast_index]]].precipitation_probability}%</div>`
: 'N/A';
]]]
wind_speed: |
[[[
const forecastEntity = states['sensor.pirate_weather_daily'];
return forecastEntity && forecastEntity.attributes && forecastEntity.attributes.forecast
? `<div class="label-small">Wind Speed</div><div>${Math.round(forecastEntity.attributes.forecast[[[forecast_index]]].wind_speed)} mph</div>`
: 'N/A';
]]]
wind_gust_speed: |
[[[
const forecastEntity = states['sensor.pirate_weather_daily'];
return forecastEntity && forecastEntity.attributes && forecastEntity.attributes.forecast
? `<div class="label-small">Wind Gusts</div><div>${Math.round(forecastEntity.attributes.forecast[[[forecast_index]]].wind_gust_speed)} mph</div>`
: 'N/A';
]]]
styles:
card:
- font-family: Roboto
- color: var(--theme-black)
- text-align: center
- padding: 0
- border: 1px solid var(--theme-grey-400)
- border-radius: 100px !important
- box-shadow: none
grid:
- grid-template-areas: >-
"icon" "day" "condition" "high_temp" "low_temp" "humidity"
"precipitation" "precipitation_probability" "wind_speed"
"wind_gust_speed"
- grid-template-columns: 1fr
- grid-template-rows: auto
custom_fields:
day:
- grid-area: day
- color: var(--theme-white)
- background-color: var(--theme-blue-400)
- font-size: 18px
- font-weight: 600
- border-radius: 100px
- padding: 0 0 0 0
- margin: 10px 5px 0 5px
icon:
- grid-area: icon
- width: 40px
- height: 40px
- margin: 10px 0 0 0
- padding: 10px
- color: white
- background-color: var(--theme-orange-300)
- border-radius: 100px
- align-self: center
- justify-self: center
condition:
- grid-area: condition
- font-size: 14px
- font-weight: 600
- text-transform: capitalize
- padding: 10px 0 5px 0
- margin: 0 0 0 0
high_temp:
- grid-area: high_temp
- font-size: 24px
- font-weight: 600
- color: var(--theme-red-800)
- background-color: var(--theme-white)
- border-bottom: 1px solid grey
- padding: 0 0 8px 0
- margin: 0 10px 0 10px
low_temp:
- grid-area: low_temp
- font-size: 24px
- font-weight: 600
- color: var(--theme-blue-800)
- padding: 4px 0 6px 0
- margin: 0 0 10px 0
humidity:
- grid-area: humidity
- font-size: 14px
- font-weight: 400
- color: var(--theme-grey-600)
- background-color: var(--theme-grey-100)
- padding: 7px 0 7px 0
precipitation:
- grid-area: precipitation
- font-size: 14px
- font-weight: 400
- color: var(--theme-grey-600)
- background-color: var(--theme-grey-200)
- padding: 7px 0 7px 0
- margin: 0 0 0 0
precipitation_probability:
- grid-area: precipitation_probability
- font-size: 14px
- font-weight: 400
- color: var(--theme-grey-600)
- background-color: var(--theme-grey-100)
- padding: 7px 0 7px 0
wind_speed:
- grid-area: wind_speed
- font-size: 13px
- font-weight: 400
- color: var(--theme-grey-600)
- padding: 6px 0 6px 0
- padding: 7px 0 7px 0
- background-color: var(--theme-grey-200)
wind_gust_speed:
- grid-area: wind_gust_speed
- font-size: 13px
- font-weight: 400
- color: var(--theme-grey-600)
- background-color: var(--theme-grey-100)
- padding: 7px 0 10px 0
card_mod:
style: |
.label-large {
font-family: Roboto;
font-size: 10px;
font-weight: 400;
color: var(--theme-grey-500);
}
.label-medium {
font-family: Roboto;
font-size: 10px;
font-weight: 400;
color: var(--theme-grey-500);
}
.label-small {
font-family: Roboto;
font-size: 12px;
font-weight: 600;
color: var(--theme-grey-800);
padding: 0 0 3px 0;
}
.symbol {
font-size: 14px;
}
.separator {
color: var(--theme-dark-grey);
font-weight: 100;
display: inline-block;
padding: 0 10px;
}