Skip to content

Instantly share code, notes, and snippets.

@idcrook
Created January 9, 2026 21:40
Show Gist options
  • Select an option

  • Save idcrook/51f27869a4ba4cd78d5cf2be8babe70e to your computer and use it in GitHub Desktop.

Select an option

Save idcrook/51f27869a4ba4cd78d5cf2be8babe70e to your computer and use it in GitHub Desktop.
Home Assistant Weather Card and Dashboard
apexcharts_card_templates:
tufte:
apex_config:
legend:
show: false
grid:
show: false
xaxis:
axisBorder:
show: false
all_series_config:
stroke_width: 1
temp_hum:
config_templates: tufte
header:
show: true
show_states: true
colorize_states: true
yaxis:
- id: temp
- id: hum
opposite: true
type: custom:grid-layout
path: weather
title: Weather
icon: mdi:weather-partly-cloudy
layout:
grid-template-columns: repeat(4, 1fr)
grid-template-areas: |-
"a b b b"
"c b b b"
"d e f g"
place-content: stretch
mediaquery:
"(max-width: 1300px)":
grid-template-columns: 1fr
grid-template-areas: |-
"a"
"b"
"c"
"d"
badges: []
cards:
- show_current: true
show_forecast: true
type: weather-forecast
entity: weather.pirateweather
#forecast_type: twice_daily
forecast_type: daily
name: Forecast
view_layout:
grid-area: a
- type: iframe
url: >-
https://embed.windy.com/embed.html?type=map&l...
aspect_ratio: 50%
view_layout:
place-self: center stretch
grid-area: b
- type: custom:apexcharts-card
config_templates:
- tufte
header:
show: true
title: Temps
show_states: true
colorize_states: true
graph_span: 36h
all_series_config:
stroke_width: 1
series:
- entity: sensor.official_weather_station_outdoor_temperature
name: Temperature
show:
extremas: true
- entity: sensor.official_weather_station_feel_likes
name: Feels Like
- entity: sensor.official_weather_station_dew_point
name: Dewpoint
view_layout:
grid-area: c
- type: custom:apexcharts-card
config_templates:
- tufte
header:
show: true
title: Wind
show_states: true
colorize_states: true
graph_span: 36h
all_series_config:
stroke_width: 1
yaxis:
- id: speed
- id: direction
opposite: true
min: 0
max: 360
decimals: 0
apex_config:
tickAmount: 8
labels:
formatter: >
EVAL:function(val, index) { if (val == 0) { return "N"; } else
if (val == 90) { return "E"; } else if (val == 180) { return
"S"; } else if (val == 270) { return "W"; } else if (val ==
360) {return "N"; } }
series:
- entity: sensor.official_weather_station_wind_speed
name: Speed
type: line
yaxis_id: speed
show:
extremas: max
group_by:
func: avg
- entity: sensor.official_weather_station_wind_gust
name: Gust
opacity: 0.2
type: area
yaxis_id: speed
show:
extremas: max
group_by:
func: max
- entity: sensor.official_weather_station_10min_avg_wind_direction
name: Direction
type: line
yaxis_id: direction
group_by:
func: avg
- type: custom:apexcharts-card
config_templates:
- tufte
header:
show: true
title: Pressure and Humidity
show_states: true
colorize_states: true
graph_span: 36h
all_series_config:
stroke_width: 1
show:
extremas: true
yaxis:
- id: pressure
decimals: 1
- id: humidity
min: 0
max: 100
decimals: 0
opposite: true
series:
- entity: sensor.official_weather_station_relative_pressure
name: Rel Pressure
yaxis_id: pressure
- entity: sensor.official_weather_station_outdoor_humidity
name: Humidity
yaxis_id: humidity
- type: custom:apexcharts-card
config_templates:
- tufte
header:
show: true
title: Solar and UVI
show_states: true
colorize_states: true
graph_span: 1d
all_series_config:
stroke_width: 1
show:
extremas: max
yaxis:
- id: radiation
- id: lux
- id: uvi
opposite: true
min: 0
max: 12
decimals: 0
series:
- entity: sensor.official_weather_station_solar_irradiance
name: Solar
yaxis_id: radiation
show:
extremas: max
- entity: sensor.official_weather_station_uv_index
name: UVI
yaxis_id: uvi
show:
extremas: max
group_by:
func: max
#type: line
opacity: 0.2
type: area
- type: custom:apexcharts-card
config_templates:
- tufte
header:
show: true
title: Rain
show_states: true
colorize_states: true
graph_span: 1d
all_series_config:
stroke_width: 1
show:
extremas: max
yaxis:
- id: rate
min: 0
max: ~0.05
decimals: 2
- id: total
min: 0
max: ~0.1
decimals: 2
opposite: true
series:
- entity: sensor.official_weather_station_piezo_rain_rate
name: Rain Rate
type: column
stroke_width: 2
yaxis_id: rate
color: rebeccapurple
- entity: sensor.official_weather_station_daily_piezo_rainfall
name: Daily Accum
yaxis_id: total
type: area
opacity: 0.2
- type: custom:plotly-graph
title: Windrose
layout:
legend:
orientation: h
margin:
t: 25
polar:
bgcolor: hsl(0% 0% 20%)
barmode: stack
bargap: 1em
radialaxis:
type: linear
ticksuffix: '%'
angle: 45
dtick: 4
color: hsl(0% 0% 80%)
angularaxis:
direction: clockwise
color: hsl(0% 0% 50%)
colorway:
- '#1984c5'
- '#22a7f0'
- '#63bff0'
- '#a7d5ed'
- '#e2e2e2'
- '#e1a692'
- '#de6e56'
- '#e14b31'
- '#c23728'
config:
displaylogo: false
hours_to_show: 24
raw_plotly_config: true
an: |-
$ex vars.theta = ( ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S',
'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'] )
fn: |-
$ex vars.windRose = (vars, minSpeed, maxSpeed) => {
// Define the headings and degree ranges for the 16 cardinal headings
const headings = [
{ label: "N", min: 350.5, max: 13.0 },
{ label: "NNE", min: 13.0, max: 35.5 },
{ label: "NE", min: 35.5, max: 58.0 },
{ label: "ENE", min: 58.0, max: 80.5 },
{ label: "E", min: 80.5, max: 103.0 },
{ label: "ESE", min: 103.0, max: 125.5 },
{ label: "SE", min: 125.5, max: 148.0 },
{ label: "SSE", min: 148.0, max: 170.5 },
{ label: "S", min: 170.5, max: 193.0 },
{ label: "SSW", min: 193.0, max: 215.5 },
{ label: "SW", min: 215.5, max: 238.0 },
{ label: "WSW", min: 238.0, max: 260.5 },
{ label: "W", min: 260.5, max: 283.0 },
{ label: "WNW", min: 283.0, max: 305.5 },
{ label: "NW", min: 305.5, max: 328.0 },
{ label: "NNW", min: 328.0, max: 350.5 }
];
// Initialize headingsCount for each heading
let headingsCount = headings.map(heading => 0);
// console.log("headingsCount Initial", headingsCount );
const observationCount = vars.windDirections.length;
// Count wind readings for each heading
// console.log("directions", vars.windDirections);
for (let i = 0; i < observationCount; i++) {
const direction = vars.windDirections[i];
const speed = vars.windSpeeds[i];
if ( (minSpeed != 0 || maxSpeed != 0) && (speed > minSpeed && speed <= maxSpeed) ) {
// Find the corresponding heading
const headingFound = headings.find(seg => {
if (seg.min < seg.max) {
return direction >= seg.min && direction <= seg.max;
} else if ( seg.min > seg.max ) {
return direction >= seg.min || direction <= seg.max;
} else {
// console.log("heading not found", i);
return false;
}
});
// Increment counter for the heading
headingsCount[headings.indexOf(headingFound)]++;
} else if (minSpeed == 0 && maxSpeed == 0 && speed == 0) {
headingsCount.forEach((_, j) => headingsCount[j]++); // increment each heading element to create a zeros "circle" at the center of the windrose plot
}
}
// Calculate percentages for headings
const percentages = headingsCount.map(count => (count / observationCount) * 100);
// console.log( "windSpeeds", vars.windSpeeds );
// console.log( "HeadingsCount", headingsCount );
// console.log( "Percentages", percentages );
return ( percentages );
}
defaults:
entity:
hovertemplate: '%{theta} %{r:.2f}%'
entities:
- entity: sensor.official_weather_station_wind_direction
internal: true
filters:
- resample: 5m
- map_y: parseFloat(y)
dn: $fn ({ ys, vars }) => { vars.windDirections = ys }
- entity: sensor.official_weather_station_wind_speed
internal: true
filters:
- resample: 5m
- map_y: parseFloat(y)
sn: $fn ({ ys, vars }) => { vars.windSpeeds = ys }
- entity: ''
type: barpolar
name: ≤5 MPH
r: $ex vars.windRose( vars, 0, 5 )
theta: $ex vars.theta
showlegend: $ex vars.windRose(vars, 0, 5).some((x) => x > 0)
- entity: ''
type: barpolar
name: ≤10 MPH
r: $ex vars.windRose( vars, 5, 10 )
theta: $ex vars.theta
showlegend: $ex vars.windRose(vars, 5, 10).some((x) => x > 0)
- entity: ''
type: barpolar
name: ≤20 MPH
r: $ex vars.windRose( vars, 10, 20 )
theta: $ex vars.theta
showlegend: $ex vars.windRose(vars, 10, 20).some((x) => x > 0)
- entity: ''
type: barpolar
name: ≤30 MPH
r: $ex vars.windRose( vars, 20, 30 )
theta: $ex vars.theta
showlegend: $ex vars.windRose(vars, 20, 30).some((x) => x > 0)
- entity: ''
type: barpolar
name: ≤40 MPH
r: $ex vars.windRose( vars, 30, 40 )
theta: $ex vars.theta
showlegend: $ex vars.windRose(vars, 30, 40).some((x) => x > 0)
- entity: ''
type: barpolar
name: ≤50 MPH
r: $ex vars.windRose( vars, 40, 50 )
theta: $ex vars.theta
showlegend: $ex vars.windRose(vars, 40, 50).some((x) => x > 0)
- entity: ''
type: barpolar
name: ≥50 MPH
r: $ex vars.windRose( vars, 50, 1000 )
theta: $ex vars.theta
showlegend: $ex vars.windRose(vars, 50, 1000).some((x) => x > 0)
---
type: custom:apexcharts-card
header:
show: true
title: Outdoor Temperature
show_states: true
colorize_states: true
graph_span: 12h
all_series_config:
stroke_width: 2
group_by:
func: avg
apex_config:
legend:
show: false
grid:
show: true
xaxis:
axisBorder:
show: false
series:
- entity: sensor.official_weather_station_outdoor_temperature
name: Temperature
show:
extremas: true
- entity: sensor.official_weather_station_feel_likes
name: Feels Like
show:
extremas: true
- entity: sensor.official_weather_station_dew_point
name: Dewpoint
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment