Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weather card without chart #1117

Merged
merged 17 commits into from Apr 26, 2018
1 change: 0 additions & 1 deletion bower.json
Expand Up @@ -15,7 +15,6 @@
"fecha": "~2.3.0",
"font-roboto-local": "~1.0.1",
"font-roboto": "PolymerElements/font-roboto-local#~1.0.1",
"google-apis": "GoogleWebComponents/google-apis#~2.0.0",
"iron-autogrow-textarea": "PolymerElements/iron-autogrow-textarea#^2.0.0",
"iron-flex-layout": "PolymerElements/iron-flex-layout#^2.0.0",
"iron-icon": "PolymerElements/iron-icon#^2.0.0",
Expand Down
332 changes: 159 additions & 173 deletions src/cards/ha-weather-card.html
@@ -1,100 +1,164 @@
<link rel='import' href='../../bower_components/polymer/polymer-element.html'>
<link rel='import' href='../../bower_components/iron-flex-layout/iron-flex-layout-classes.html'>
<link rel='import' href='../../bower_components/google-apis/google-legacy-loader.html'>
<link rel='import' href='../../bower_components/iron-icon/iron-icon.html'>
<link rel='import' href='../components/ha-card.html'>

<link rel='import' href='../util/hass-mixins.html'>

<dom-module id='ha-weather-card'>
<template>
<style>
.content {
padding: 0 16px 16px;
}

.attribution {
color: var(--secondary-text-color);
text-align: right;
}
.condicon {
text-align: center;
font-size: 4em;
}
.condtemp {
text-align: center;
font-size: 4em;
}
div.cond {
display: inline;
}
div.conddetails {
float: right;
display: block table;
overflow: auto;
text-align: right;
}
span.line {
display: block;
}
.condsmall {
font-size: 1em;
}
.clear {
clear: both;
}
.content {
padding: 0 16px 16px;
}

iron-icon {
color: var(--paper-item-icon-color);
}

.flex {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
}

.now iron-icon,
.now div,
.attributes div,
.forecast div {
flex: 1;
}

.now .state {
--iron-icon-height: 48px;
--iron-icon-width: 48px;
font-size: 32px;
}

.now .temp {
font-size: 48px;
text-align: right;
flex: 0 0 150px;
}

.attributes,
.forecast {
margin-top: 24px;
}

.attribute {
min-width: calc(50% - 16px);
max-width: calc(50% - 16px);
}

.icon-container {
width: 50%;
margin-right: 8px;
text-align: center;
}

.weekday,
.temp,
.templow {
text-align: center;
}

.attributes iron-icon,
.forecast iron-icon {
width: 100%;
}

.forecast iron-icon {
margin: 8px auto;
}

.weekday {
font-weight: bold;
}

.templow {
color: var(--secondary-text-color);
}
</style>
<google-legacy-loader on-api-load='googleApiLoaded'></google-legacy-loader>
<ha-card header='[[computeTitle(stateObj)]]'>
<ha-card header='[[stateObj.attributes.friendly_name]]'>
<div class='content'>
<div class='condpanel'>
<div class='cond condicon'>[[nowCond]]</div>
<div class='cond condtemp'>[[attr.temperature]]°</div>
<div class='cond conddetails'>
<div class="condsmall" hidden$="[[!attr.wind_speed]]">
[[localize('attribute.weather.wind_speed')]]:
<span hidden$="[[!attr.wind_speed]]">
[[attr.wind_speed]]
</span>
<span hidden$="[[!windBearing]]">[[windBearing]]</span>
<div class="now flex">
<div class="state">
<iron-icon icon="[[getWeatherIcon(stateObj.state)]]"></iron-icon>
[[computeState(stateObj.state, localize)]]
</div>
<div class="temp">[[getTemperature(stateObj.attributes.temperature)]]</div>
</div>

<div class="attributes flex">
<div class="attribute flex">
<div class="icon-container">
<iron-icon icon="mdi:water-percent"></iron-icon>
<div>[[localize('attribute.weather.humidity')]]</div>
</div>
<div class="condsmall" hidden$="[[!attr.humidity]]">
[[localize('attribute.weather.humidity')]]: [[attr.humidity]]%
<div>[[stateObj.attributes.humidity]] %</div>
</div>
<div class="attribute flex">
<div class="icon-container">
<iron-icon icon="mdi:flag-variant"></iron-icon>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mdi:weather-windy may be a better icon for wind_speed.

<div>[[localize('attribute.weather.wind_speed')]]</div>
</div>
<div class="condsmall" hidden$="[[!attr.visibility]]">
[[localize('attribute.weather.visibility')]]: [[attr.visibility]]
<div>[[getWind(stateObj.attributes.wind_speed, stateObj.attributes.wind_bearing, localize)]]</div>
</div>
<div class="attribute flex">
<div class="icon-container">
<iron-icon icon="mdi:eye"></iron-icon>
<div>[[localize('attribute.weather.visibility')]]</div>
</div>
<div>[[getVisibilty(stateObj.attributes.visibility)]]</div>
</div>
<div class='clear'></div>
<template is='dom-if' if='[[stateObj.attributes.pressure]]'>
<div class="attribute flex">
<div class="icon-container">
<iron-icon icon="mdi:gauge"></iron-icon>
<div>[[localize('attribute.weather.air_pressure')]]</div>
</div>
<div>[[stateObj.attributes.pressure]] hPa</div>
</div>
</template>
</div>
<div id='chart_id' hidden$="[[!attr.forecast]]"></div>

<template is='dom-if' if='[[stateObj.attributes.forecast]]'>
<div class="forecast flex">
<template is='dom-repeat' items='[[stateObj.attributes.forecast]]'>
<div>
<div class="weekday">[[getWeekday(item.datetime)]]</div>
<iron-icon icon="[[getWeatherIcon(item.condition)]]"></iron-icon>
<div class="temp">[[item.temperature]]°</div>
<div class="templow">[[item.templow]]°</div>
</div>
</template>
</div>
</template>
</div>
</ha-card>
</template>
</dom-module>

<script>
{
var _WEATHER_TO_ICON = {
cloudy: '\u2601\ufe0f',
fog: '\uD83C\uDF2B\ufe0f',
hail: 'Hail',
lightning: '\uD83C\uDF29\ufe0f',
'lightning-rainy': '\u26c8\ufe0f',
partlycloudy: '\u26c5\ufe0f',
pouring: '\uD83D\uDCA7\ufe0f',
rainy: '\uD83C\uDF27\ufe0f',
snowy: '\uD83C\uDF28\ufe0f',
'snowy-rainy': '\uD83C\uDF28\ufe0f',
sunny: '\u2600\ufe0f',
windy: '\uD83C\uDF2C\ufe0f',
'windy-variant': '\uD83C\uDF2C\ufe0f',
exceptional: '\u2b55\ufe0f',
const _WEATHER_TO_ICON = {
'clear-night': 'mdi:weather-night',
cloudy: 'mdi:weather-cloudy',
fog: 'mdi:weather-fog',
hail: 'mdi:weather-hail',
lightning: 'mid:weather-lightning',
'lightning-rainy': 'mdi:weather-lightning-rainy',
partlycloudy: 'mdi:weather-partlycloudy',
pouring: 'mdi:weather-pouring',
rainy: 'mdi:weather-rainy',
snowy: 'mdi:weather-snowy',
'snowy-rainy': 'mdi:weather-snowy-rainy',
sunny: 'mdi:weather-sunny',
windy: 'mdi:weather-windy',
'windy-variant': 'mdi:weather-windy-variant'
};

var _DEGREE_TEXT = [
'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N'
];
/*
* @appliesMixin window.hassMixins.LocalizeMixin
*/
Expand All @@ -103,120 +167,42 @@
static get properties() {
return {
hass: Object,
stateObj: {
type: Object,
observer: 'checkRequirements'
},
attr: Object,
windBearing: Object,
nowCond: Object,
stateObj: Object
};
}

computeTitle(stateObj) {
return stateObj.attributes.friendly_name;
getUnit(unit) {
return this.hass.config.core.unit_system[unit] || '';
}
getDataArray() {
var dataArray = [];
var data = this.stateObj.attributes.forecast;
var i;

if (!this.stateObj.attributes.forecast) {
return [];
}

for (i = 0; i < data.length; i++) {
var d = data[i];
if (!d.condition) {
dataArray.push([new Date(d.datetime), null, d.temperature, d.templow]);
} else {
dataArray.push([new Date(d.datetime), _WEATHER_TO_ICON[data[i].condition],
d.temperature, d.templow]);
}
}

return dataArray;
computeState(state, localize) {
return localize(`state.weather.${state.replace('-', '_')}`) || state;
}

checkRequirements() {
if (!this.stateObj) {
return;
}

if (!this.stateObj.attributes) {
return;
}

this.attr = this.stateObj.attributes;

this.nowCond = _WEATHER_TO_ICON[this.stateObj.state];
this.windBearing = this.windBearingToText(this.attr.wind_bearing);

if (!window.google || !window.google.visualization) {
return;
}

if (!this.chartEngine) {
this.chartEngine = new window.google.visualization.LineChart(this.$.chart_id);
}

if (!this.attr.forecast) {
return;
}

this.drawChart();
getWeatherIcon(weather) {
return _WEATHER_TO_ICON[weather];
}
drawChart() {
var dataGrid = new window.google.visualization.DataTable();
var options = {
legend: {
position: 'top'
},
interpolateNulls: true,
titlePosition: 'none',
chartArea: {
left: 25,
top: 5,
height: '100%',
width: '90%',
bottom: 25,
},
curveType: 'function',
focusTarget: 'category',
colors: ['red', 'blue'],
annotations: {
textStyle: {
fontSize: '24',
}
}
};

dataGrid.addColumn('datetime', 'Time');
dataGrid.addColumn({ type: 'string', role: 'annotation' });
dataGrid.addColumn('number', 'Temperature');
dataGrid.addColumn('number', 'Temperature Low');

var dataArray = this.getDataArray();
dataGrid.addRows(dataArray);
getTemperature(temp) {
return `${temp}${this.getUnit('temperature')}`;
}

this.chartEngine.draw(dataGrid, options);
getWind(speed, bearing, localize) {
if (!bearing) {
return `${speed} ${this.getUnit('length')}/h`;
}
const degreeTranslation = JSON.parse(localize('attribute.weather.wind_bearing'));
const degreeText = degreeTranslation[(((parseInt(bearing) + 11.25) / 22.5) | 0) % 16];
return `${speed} ${this.getUnit('length')}/h (${degreeText})`;
}

googleApiLoaded() {
window.google.load('visualization', '1', {
packages: ['corechart'],
callback: function () {
this.checkRequirements();
}.bind(this),
});
getVisibilty(visibilty) {
return `${visibilty} ${this.getUnit('length')}`;
}

windBearingToText(degree) {
var degreenum = parseInt(degree);
if (isFinite(degreenum)) {
return _DEGREE_TEXT[(((degreenum + 11.25) / 22.5) | 0) % 16];
}
return '';
getWeekday(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString(window.navigator, { weekday: 'short' });
}
}
customElements.define(HaWeatherCard.is, HaWeatherCard);
Expand Down