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

[WIP] Weather module #504

Closed
wants to merge 14 commits into from
1 change: 0 additions & 1 deletion front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"immutability-helper": "^3.0.0",
"leaflet": "^1.4.0",
"linkstate": "^1.1.1",
"moment": "^2.24.0",
"preact": "^8.2.6",
"preact-cli-plugin-fast-async": "^1.0.1",
"preact-compat": "^3.18.4",
Expand Down
43 changes: 39 additions & 4 deletions front/src/actions/dashboard/boxes/weather.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,52 @@ import get from 'get-value';

const BOX_KEY = 'Weather';

function createActions(store) {
const translateWeatherToFeIcon = weather => {
if (weather === 'snow') {
return 'fe-cloud-snow';
}
if (weather === 'rain') {
return 'fe-cloud-rain';
}
if (weather === 'clear') {
return 'fe-sun';
}
if (weather === 'cloud') {
return 'fe-cloud';
}
if (weather === 'fog') {
return 'fe-cloud';
}
if (weather === 'sleet') {
return 'fe-cloud-drizzle';
}
if (weather === 'wind') {
return 'fe-wind';
}
if (weather === 'night') {
return 'fe-moon';
}
return 'fe-question';
};

const createActions = store => {
const boxActions = createBoxActions(store);

const actions = {
async getWeather(state, box, x, y) {
boxActions.updateBoxStatus(state, BOX_KEY, x, y, RequestStatus.Getting);
try {
const weather = await state.httpClient.get(`/api/v1/house/${box.house}/weather`);
const weather = await state.httpClient.get(`/api/v1/house/${box.house}/weather?mode=${box.mode}`);
weather.datetime_beautiful = dayjs(weather.datetime)
.locale(state.user.language)
.format('D MMM');
.format('dddd DD MMMM');
weather.temperature = weather.temperature.toFixed(2);
weather.weather = translateWeatherToFeIcon(weather.weather);
if (box.mode === 'advanced') {
weather.hours.map(day => {
day.weather = translateWeatherToFeIcon(day.weather);
});
}
boxActions.mergeBoxData(state, BOX_KEY, x, y, {
weather
});
Expand All @@ -36,6 +71,6 @@ function createActions(store) {
}
};
return Object.assign({}, actions);
}
};

export default createActions;
34 changes: 33 additions & 1 deletion front/src/components/boxs/weather/EditWeatherBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { connect } from 'unistore/preact';
import { Text } from 'preact-i18n';
import actions from '../../../actions/dashboard/edit-boxes/editWeather';
import BaseEditBox from '../baseEditBox';
import { GetWeatherModes } from '../../../utils/consts';

const EditWeatherBox = ({ children, ...props }) => (
<BaseEditBox {...props} titleKey="dashboard.boxTitle.weather">
Expand All @@ -22,6 +23,30 @@ const EditWeatherBox = ({ children, ...props }) => (
))}
</select>
</div>
<div class="form-group">
<div>
<label>
<Text id="dashboard.boxes.weather.editModeLabel" />
</label>
</div>
<div>
{GetWeatherModes.map(mode => {
const label = 'dashboard.boxes.weather.displayModes.' + mode;
return (
<label>
<input
type="radio"
name="mode"
value={mode}
checked={mode === props.box.mode}
onChange={props.updateBoxMode}
/>
&nbsp; <Text id={label} />
</label>
);
})}
</div>
</div>
</BaseEditBox>
);

Expand All @@ -35,12 +60,19 @@ class EditWeatherBoxComponent extends Component {
house: e.target.value
});
};

updateBoxMode = e => {
this.props.updateBoxConfig(this.props.x, this.props.y, {
mode: e.target.value
});
};

componentDidMount() {
this.props.getHouses();
}

render(props, {}) {
return <EditWeatherBox {...props} updateBoxHouse={this.updateBoxHouse} />;
return <EditWeatherBox {...props} updateBoxHouse={this.updateBoxHouse} updateBoxMode={this.updateBoxMode} />;
}
}

Expand Down
160 changes: 129 additions & 31 deletions front/src/components/boxs/weather/WeatherBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component } from 'preact';
import { connect } from 'unistore/preact';
import { Text } from 'preact-i18n';
import { Link } from 'preact-router/match';
import dayjs from 'dayjs';
import actions from '../../../actions/dashboard/boxes/weather';
import {
RequestStatus,
Expand All @@ -18,7 +19,7 @@ const padding = {
paddingBottom: '10px'
};

const BOX_REFRESH_INTERVAL_MS = 30 * 60 * 1000;
const BOX_REFRESH_INTERVAL_MS = 10 * 60 * 1000;

const WeatherBox = ({ children, ...props }) => (
<div class="card">
Expand Down Expand Up @@ -97,19 +98,20 @@ const WeatherBox = ({ children, ...props }) => (
)}
{props.weather && (
<div style={padding} class="card-block px-30 py-10">
<div
style={{
fontSize: '14px',
color: '#76838f'
}}
>
{props.datetimeBeautiful} - {props.houseName}
</div>
<div class="row">
<div class="col-6">
<div
style={{
fontSize: '14px',
color: '#76838f'
}}
>
{props.datetimeBeautiful}
</div>
<div class="col-9">
<div
style={{
fontSize: '40px'
fontSize: '40px',
lineHeight: '1.2'
}}
class="font-size-40 blue-grey-700"
>
Expand All @@ -124,37 +126,72 @@ const WeatherBox = ({ children, ...props }) => (
</div>
</div>
<div
class="col-6 text-right"
class="col-3 text-right"
style={{
padding: '10px'
paddingRight: '10px',
paddingLeft: '10px',
marginTop: '-0.75rem'
}}
>
{props.weather === 'rain' && (
<i
className={'fe ' + props.weather}
style={{
fontSize: '60px'
}}
/>
</div>
</div>
{props.display === 'advanced' && (
<div class="col-9" style={{ padding: '0' }}>
<span>
<i
class="fe fe-cloud-drizzle"
class="fe fe-droplet"
style={{
fontSize: '60px'
paddingRight: '5px'
}}
/>
)}
{props.weather === 'sun' && (
<i
class="fe fe-sun"
{props.humidity}
<span
style={{
fontSize: '60px'
fontSize: '12px',
color: 'grey'
}}
/>
)}
{props.weather === 'cloud' && (
>
%
</span>
</span>
<span style={{ float: 'right' }}>
<i
class="fe fe-cloud-drizzle"
class="fe fe-wind"
style={{
fontSize: '60px'
paddingRight: '5px'
}}
/>
)}
{props.wind}
<span
style={{
fontSize: '12px',
color: 'grey'
}}
>
{props.units === 'si' ? 'km/h' : 'm/h'}
</span>
</span>
</div>
</div>
)}
{props.display === 'advanced' && (
<div>
{props.alert_display}
<div
class="row"
style={{
marginTop: '0.5em'
}}
>
{props.hours_display}
</div>
</div>
)}
</div>
)}
</div>
Expand All @@ -176,11 +213,67 @@ class WeatherBoxComponent extends Component {
const boxData = get(props, `${DASHBOARD_BOX_DATA_KEY}Weather.${props.x}_${props.y}`);
const boxStatus = get(props, `${DASHBOARD_BOX_STATUS_KEY}Weather.${props.x}_${props.y}`);
const weatherObject = get(boxData, 'weather');
const weather = get(weatherObject, 'weather');
const temperature = Math.round(get(weatherObject, 'temperature'));
const units = get(weatherObject, 'units');

const display = this.props.box.mode || 'basic';
const datetimeBeautiful = get(weatherObject, 'datetime_beautiful');
const units = get(weatherObject, 'units');
const temperature = get(weatherObject, 'temperature');
const houseName = get(weatherObject, 'house.name');
let weather = get(weatherObject, 'weather');

let humidity, wind, alert_display, hours_display;

if (display === 'advanced') {
humidity = get(weatherObject, 'humidity');
wind = get(weatherObject, 'wind_speed');
const alert = get(weatherObject, 'alert');
if (units === 'si') {
wind = wind * 3.6;
wind = wind.toFixed(2);
}
if (typeof alert != 'undefined' && alert !== null) {
let color = '#FFD6D4';
if (alert.severity === 'warning') {
color = '#FF8D87';
}
alert_display = (
<div
class="row"
style={{ fontSize: '0.75em', marginTop: '0.5em', backgroundColor: color, borderRadius: '3px' }}
>
<p style={{ margin: '2px', fontWeight: 'bold' }}>{alert.title}</p>
<p style={{ margin: '2px' }}>{alert.description}</p>
</div>
);
}
const hours = get(weatherObject, 'hours');
if (typeof hours !== 'undefined') {
let i = 0;
hours_display = hours.map(hour => {
const borderStyle = {};
if (i === 3) {
borderStyle.borderRight = '0.01em dotted grey';
borderStyle.marginRight = '0';
}
if (i === 4) {
borderStyle.borderLeft = '0.01em dotted grey';
borderStyle.marginLeft = '0';
}
i += 1;
return (
<div style={Object.assign({ width: '10%', margin: '0.25em 1.25%' }, borderStyle)}>
<p style={{ margin: 'auto', textAlign: 'center', fontSize: '10px', color: 'grey' }}>
{dayjs(hour.datetime).format('HH')}h
</p>
<p style={{ margin: 'auto', textAlign: 'center' }}>
<i className={' fe ' + hour.weather} style={{ fontSize: '20px' }} />
</p>
<p style={{ margin: 'auto', textAlign: 'center', fontSize: '12px' }}>{hour.temperature}&deg;</p>
</div>
);
});
}
}
return (
<WeatherBox
{...props}
Expand All @@ -190,6 +283,11 @@ class WeatherBoxComponent extends Component {
boxStatus={boxStatus}
datetimeBeautiful={datetimeBeautiful}
houseName={houseName}
hours_display={hours_display}
humidity={humidity}
wind={wind}
alert_display={alert_display}
display={display}
/>
);
}
Expand Down
7 changes: 6 additions & 1 deletion front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,12 @@
"serviceNotConfigured": "The DarkSky service is not configured. Please go to the 'Integrations' tab, and configure the DarkSky service.",
"requestToThirdPartyFailed": "The request to DarkSKy API failed. Is your Gladys instance connected to the internet? Please go to the DarkSky configuration panel to troubleshoot this problem.",
"clickHere": "Click here to access the DarkSky configuration panel.",
"unknownError": "We are unable to get the weather for this house. Did you define a house for this box?"
"unknownError": "We are unable to get the weather for this house. Did you define a house for this box?",
"editModeLabel": "Select the mode you want to display.",
"displayModes": {
"basic": "Basic : temperature",
"advanced": "Advanced : temperature, humidity, wind speed, previsions for the next 8 hours and forecast alerts"
}
},
"devicesInRoom": {
"editRoomLabel": "Select the room you want to display here."
Expand Down
2 changes: 1 addition & 1 deletion front/src/routes/integration/all/darksky/DarkSky.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const DarkSkyPage = ({ children, ...props }) => (
class={cx('btn', 'btn-success', {
'btn-loading': props.loading
})}
onClick={props.saveApiKey}
onClick={props.saveConfig}
type="button"
>
<Text id="integration.darkSky.saveButton" />
Expand Down
Loading