diff --git a/.env.default b/.env.default index 493a9b48c4c579d488bdbd189fb327bd7324d480..5559bc762c4903b23b6505d5f6462ce4c2cf1afe 100644 --- a/.env.default +++ b/.env.default @@ -14,6 +14,7 @@ REACT_APP_PROJECT_SERVICE REACT_APP_REPORT_SERVICE REACT_APP_IOT_SERVICE REACT_APP_USER_SERVICE +REACT_APP_WEATHER_SERVICE REACT_APP_GOOGLE_CLIENT REACT_APP_COUCH_DB_URL REACT_APP_BLOCLINK_URL diff --git a/README.md b/README.md index 15ad3e5f13ff4f301b38a90bf59989dbcf240199..624862b69f00e64ddc12b4b766e3fa47dd240629 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ REACT_APP_DOCUMENT_SERVICE REACT_APP_BUILDING_SERVICE REACT_APP_UTILITY_SERVICE REACT_APP_REPORT_SERVICE +REACT_APP_WEATHER_SERVICE REACT_APP_BLOCLINK_URL REACT_APP_BLOCNOTE_URL REACT_APP_AUTH0_DOMAIN diff --git a/src/containers/Sensors/SensorGraph.js b/src/containers/Sensors/SensorGraph.js index 29aa3eea9074a5a2834a2baba4fd2633b5bb8b4d..61ab7d266bea243a011cd79011bf1df5561a5bfe 100644 --- a/src/containers/Sensors/SensorGraph.js +++ b/src/containers/Sensors/SensorGraph.js @@ -16,6 +16,7 @@ import { import { TimeSeries, TimeRange } from 'pondjs'; import { Icon } from 'react-fa'; import { loadAllNodeData } from './actions'; +import { loadWeather } from '../Weather/actions'; import { subtractDaysFromNow } from '../../utils/date'; import { GRAPH_COLORS } from './colors'; @@ -29,6 +30,7 @@ export class SensorGraph extends Component { this.state = { timeseries: new TimeSeries(), + weatherTimeseries: new TimeSeries(), timerange: new TimeRange([subtractDaysFromNow(this.NUM_DAYS - 1), today]), showChart: false, from: subtractDaysFromNow(this.NUM_DAYS), @@ -45,6 +47,11 @@ export class SensorGraph extends Component { from: this.state.from.toUTCString(), unit_id: 1, // Temperature }); + this.props.loadWeather({ + measurement: 'temperature', + interval: 'hourly', + location: 'New_York:NY', + }); } componentWillReceiveProps(nextProps) { @@ -64,9 +71,9 @@ export class SensorGraph extends Component { } const { sensorData } = props.sensors; - let nodes = []; + let lines = []; sensorData.forEach(gateway => { - nodes = [...nodes, ...gateway.nodes.reduce((acc, oneNode) => { + lines = [...lines, ...gateway.nodes.reduce((acc, oneNode) => { if (!oneNode.temperature_probe_1 && !oneNode.temperature_probe_2 && !oneNode.temperature_probe_3 && !oneNode.temperature_probe_4) { acc.push(this.generateTimeSeries(oneNode)); @@ -75,19 +82,30 @@ export class SensorGraph extends Component { }, [])]; }); - if (nodes.length === 0) { + const weatherPoints = this.props.weather.weatherData.map(val => ( + [new Date(val.time), val.fields.value] + )); + if (weatherPoints.length > 0) { + lines.push(new TimeSeries({ + name: 'Outdoor Temperature', + columns: ['time', 'Outdoor Temperature'], + points: weatherPoints, + })); + } + + if (lines.length === 0) { return; } const styles = {}; - nodes.forEach((line, index) => { + lines.forEach((line, index) => { styles[line.name()] = this.generateLineStyle(line, index); }); this.setState({ - timeseries: nodes, + timeseries: lines, showChart: true, lineStyles: styles, - cat: this.generateCategories(nodes), + cat: this.generateCategories(lines), }); } @@ -285,7 +303,7 @@ export class SensorGraph extends Component { ( { sensors: state.sensors, buildingArea: state.buildingArea, + weather: state.weather, } ); const mapDispatchToProps = dispatch => ( bindActionCreators({ loadAllNodeData, + loadWeather, }, dispatch) ); diff --git a/src/containers/Weather/actions.js b/src/containers/Weather/actions.js new file mode 100644 index 0000000000000000000000000000000000000000..6d334df7cb883d2df782bba5e74f071f0b0c306f --- /dev/null +++ b/src/containers/Weather/actions.js @@ -0,0 +1,13 @@ +import { + WEATHER_REQUESTED, + WEATHER_SUCCEEDED, + WEATHER_FAILED, +} from './constants'; + +import { makeActionCreator } from '../../utils/reduxHelpers'; + +/* Retrieve Weather */ +export const loadWeather = makeActionCreator(WEATHER_REQUESTED, 'filters'); +export const weatherLoaded = makeActionCreator(WEATHER_SUCCEEDED, 'weatherData'); +export const weatherFailed = makeActionCreator(WEATHER_FAILED, 'error'); + diff --git a/src/containers/Weather/constants.js b/src/containers/Weather/constants.js new file mode 100644 index 0000000000000000000000000000000000000000..95ea0d6e20754c3a0776d34ff5668f44a83d8efd --- /dev/null +++ b/src/containers/Weather/constants.js @@ -0,0 +1,4 @@ +/* Retrieve Weather */ +export const WEATHER_REQUESTED = 'WEATHER_REQUESTED'; +export const WEATHER_SUCCEEDED = 'WEATHER_SUCCEEDED'; +export const WEATHER_FAILED = 'WEATHER_FAILED'; diff --git a/src/containers/Weather/reducer.js b/src/containers/Weather/reducer.js new file mode 100644 index 0000000000000000000000000000000000000000..bda12282ab838e2f7900f24b6dad2f3a8bad722f --- /dev/null +++ b/src/containers/Weather/reducer.js @@ -0,0 +1,32 @@ +import { + WEATHER_REQUESTED, + WEATHER_SUCCEEDED, + WEATHER_FAILED, +} from './constants'; + +export const initState = { + weatherLoading: false, + weatherError: false, + weatherData: [], +}; + +export default (state = initState, action) => { + switch (action.type) { + case WEATHER_REQUESTED: + return { ...state, weatherLoading: true, weatherError: false }; + + case WEATHER_SUCCEEDED: + return { + ...state, + weatherLoading: false, + weatherError: false, + weatherData: action.weatherData.data, + }; + + case WEATHER_FAILED: + return { ...state, weatherLoading: false, weatherError: action.error }; + + default: + return state; + } +}; diff --git a/src/containers/Weather/sagas.js b/src/containers/Weather/sagas.js new file mode 100644 index 0000000000000000000000000000000000000000..c2e7c5c3f411e05236041bd1fcff523d9a7f8885 --- /dev/null +++ b/src/containers/Weather/sagas.js @@ -0,0 +1,23 @@ +import { takeEvery } from 'redux-saga/effects'; +import SagaRequests from '../../utils/sagaRequests'; +import { weatherURL } from '../../utils/restServices'; + +import { + WEATHER_REQUESTED, +} from './constants'; + +import { + weatherLoaded, + weatherFailed, +} from './actions'; + +function* getWeather(action) { + yield SagaRequests.get(action, weatherURL, weatherLoaded, weatherFailed); +} + + +function* weatherWatcher() { + yield takeEvery(WEATHER_REQUESTED, getWeather); +} + +export default weatherWatcher; diff --git a/src/reducers.js b/src/reducers.js index 01c73d6785a31e40ac72990248102dfb7419d8b5..01448c9569ae32d43bef5ea58b1fe81fd87d4d53 100644 --- a/src/reducers.js +++ b/src/reducers.js @@ -14,6 +14,7 @@ import bGroup from './containers/BGroup/reducer'; import user from './containers/User/reducer'; import sensors from './containers/Sensors/reducer'; import buildingArea from './containers/BuildingArea/reducer'; +import weather from './containers/Weather/reducer'; export default combineReducers({ @@ -31,4 +32,5 @@ export default combineReducers({ user, sensors, buildingArea, + weather, }); diff --git a/src/sagas.js b/src/sagas.js index 01b25462f85ce4c5dad970be9b8122ca26636ecf..7ef7849ac909f44fa203273ad145f1153bb9778f 100644 --- a/src/sagas.js +++ b/src/sagas.js @@ -11,6 +11,7 @@ import bGroupSaga from './containers/BGroup/sagas'; import userSaga from './containers/User/sagas'; import sensorsSaga from './containers/Sensors/sagas'; import buildingAreaSaga from './containers/BuildingArea/sagas'; +import weatherSaga from './containers/Weather/sagas'; export default function* rootSaga() { @@ -28,5 +29,6 @@ export default function* rootSaga() { userSaga(), sensorsSaga(), buildingAreaSaga(), + weatherSaga(), ]; } diff --git a/src/utils/restServices.js b/src/utils/restServices.js index ce0de66345e66057b37b3c08b3e6067739ec452c..18aad52a20a5f45fa7f8336898e4aa37577159f0 100644 --- a/src/utils/restServices.js +++ b/src/utils/restServices.js @@ -12,6 +12,7 @@ export const projectService = process.env.REACT_APP_PROJECT_SERVICE; const reportService = process.env.REACT_APP_REPORT_SERVICE; const iotService = process.env.REACT_APP_IOT_SERVICE; const userService = process.env.REACT_APP_USER_SERVICE; +const weatherService = process.env.REACT_APP_WEATHER_SERVICE; export const buildingsURL = `${buildingService}/building/`; export const apartmentsURL = `${buildingService}/apartment/`; @@ -49,3 +50,5 @@ export const sensorImageURL = `${iotService}/sensorimage/`; export const userURL = `${userService}/user/`; export const userGroupsURL = `${userService}/usergroup/`; + +export const weatherURL = `${weatherService}/weather/`;