
A smart home application that brings you new ways to track consumption cost and practice sustainable living.
Explore the docs »
View Demo
Table of Contents
HomeE, is a smart home system that could be integrated into smart home devices like Google Nest Hub and other computer appliances including mobile and web-based user interfaces. The current technical prototype is delivered as a web application to promote the concept but also prove the potential for future development.
Front End
Back End(NOT doing but already setup)
Deployment
To get a local copy up and running, please follow these simple steps below.
- npm
npm install npm@latest -g
- Clone the repo
git clone https://github.com/nancywan1004/home-e.git
- Install NPM packages
npm install --all
- Run the project
npm start
The utility rings were drawn based on the DonutChart of Chartjs, while requiring extra plugin and option configurations to customize the default chart(e.g. display the texts inside the circle).
Here's the sample code in src/components/DonutChart.tsx
Plugins | Options |
---|---|
const plugins: any = [{
beforeDraw: function(chart: any) {
let width = chart.width,
height = chart.height,
ctx = chart.ctx;
ctx.restore();
let fontSize = (height / 300).toFixed(2);
ctx.font = fontSize + "rem Futura, sans-serif";
ctx.textBaseline = "top";
// Text for current week's cost
let costText = "$" + props.cost.toFixed(2),
costTextX = Math.round((width - ctx.measureText(costText).width) / 2),
costTextY = height / 1.75;
// Text for this week's remaining value(i.e. budget - current cost)
let remainingText = `$${props.target - props.cost < 0 ? 0.00.toFixed(2) : (props.target - props.cost).toFixed(2)} remaining`,
remainingTextX = Math.round((width - ctx.measureText(remainingText).width) / 2),
remainingTextY = height / 1.25;
// Icon image for the utility type
let icon = new Image();
icon.src = props.iconUrl;
ctx.drawImage(icon, width / 2.25, height / 2.45, width / 10, height / 8);
ctx.fillText(costText, costTextX, costTextY);
ctx.fillText(remainingText, remainingTextX, remainingTextY);
ctx.save();
},
beforeInit: (chart: any) => {
const dataset = chart.data.datasets[0];
chart.data.labels = [dataset.label];
dataset.data = [dataset.percent, 100 - dataset.percent < 0 ? 0 : 100 - dataset.percent];
}
}] |
const options: any = {
plugins: {
legend: {
display: false
},
tooltip: {
filter: function(tooltipItem: any) {
return !!tooltipItem.label;
},
callbacks: {
label: function(chart: any) {
var dataset = chart.dataset;
var currentValue = dataset.data[chart.datasetIndex];
return currentValue + '%';
}
}
},
},
cutout: "75%",
radius: "50%",
rotation: Math.PI * -0.5
} |
<Doughnut data={props.utilityData} width={100} height={100} options={options} plugins={plugins}/>
The shaded area under each ring indicating the weekly utility trend was created based on LineChart of Chartjs. One big challenge of using the AreaChart directly was to fill the area while retaining the round corner of the card container. This would then require a workaround using the native Canvas arcTo() method
for articulated area calculations. The drawing time of this shaded area occurs at the beforeDraw
stage. For more information on the rendering pipeline of the plugins, please refer to Rendering Documentations.
Here's the sample code in src/components/AreaChart.tsx
:
const plugins: any = [{
beforeDraw: function(chart: any) {
let width = chart.width,
height = chart.height,
ctx = chart.ctx;
let meta = chart.getDatasetMeta(0);
let lastWeekDatapoint = meta.data[0];
let thisWeekDatapoint = meta.data[1];
ctx.restore();
ctx.beginPath();
// Create a starting point
ctx.moveTo(lastWeekDatapoint.x - 0.05 * width, lastWeekDatapoint.y);
if (lastWeekDatapoint.y <= thisWeekDatapoint.y) {
// Create left vertical line
ctx.lineTo(lastWeekDatapoint.x - 0.05 * width, lastWeekDatapoint.y + height * 0.35);
// Create left bottom arc
ctx.arcTo(lastWeekDatapoint.x - 0.05 * width, lastWeekDatapoint.y + height, lastWeekDatapoint.x + width * 0.3, lastWeekDatapoint.y + height, 60);
// Continue wiht horizontal line
ctx.lineTo(lastWeekDatapoint.x + width * 0.5, lastWeekDatapoint.y + height);
// Create right bottom arc
ctx.arcTo(lastWeekDatapoint.x + width, lastWeekDatapoint.y + height, lastWeekDatapoint.x + width, lastWeekDatapoint.y + height * 0.35, 55);
} else {
// Create left vertical line
ctx.lineTo(lastWeekDatapoint.x - 0.05 * width, lastWeekDatapoint.y + height * 0.35);
// Create left bottom arc
ctx.arcTo(lastWeekDatapoint.x - 0.05 * width, lastWeekDatapoint.y + height * 0.9, lastWeekDatapoint.x + width * 0.5, lastWeekDatapoint.y + height * 0.9, 80);
// Continue with horizontal line
ctx.lineTo(lastWeekDatapoint.x + width * 0.5, lastWeekDatapoint.y + height * 0.9);
// Create right bottom arc
ctx.arcTo(lastWeekDatapoint.x + width, lastWeekDatapoint.y + height * 0.9, lastWeekDatapoint.x + width, lastWeekDatapoint.y + height * 0.35, 70);
}
// Continue with right vertical line and end path
ctx.lineTo(lastWeekDatapoint.x + width, thisWeekDatapoint.y);
ctx.fillStyle = fillColor(lastWeekDatapoint, thisWeekDatapoint) + "40";
ctx.fill();
ctx.save();
},
}]
The bar charts on the three subpages were built upon the BarChart of Chartjs. In order to incorporate this week and last week's data, two x axes were needed, thus requiring xAxisID to be specified. This would allow the chart renderer to know which axis to plot this dataset on, and add customized styling accordingly.
In addition, the daily/weekly/monthly tabs were then implemented with the Tabs API
of the material-ui library.
Here's the sample code in src/components/subpage/BarChart.tsx
:
<Tabs value={data} onChange={handleChange} className={classes.timeTabs} TabIndicatorProps={{style: {display: "none"}}}>
<Tab className={classes.timeTab} value={dailyData} label="Day" />
<Tab className={classes.timeTab} value={weeklyData} label="Week" />
<Tab className={classes.timeTab} value={monthlyData} label="Month" />
</Tabs>
<Bar data={data} options={options} plugins={[ChartDataLabels] as any} ref={barChart}/>
Note: For more troubleshooting on the Chartjs API, please refer to 3.x.x Migration Documentation.
- Dashboard (
src/App.tsx
)- Welcome board (
src/components/WelcomePanel.tsx
) - Rings (
src/components/DonutPanel.tsx
) - Control Center (
src/components/controlCenter/*
) - Achievement System (
src/components/AchievementPanel.tsx
)
- Welcome board (
- Subpage (
src/components/subpage
)- Utility trend bar chart(daily/weekly/monthly) (
src/components/subpage/BarChart.tsx
) - Budget setting panel (
src/components/subpage/BudgetSettingPanel.tsx
) - Budget setting popup (
src/components/subpage/BudgetSettingPopup.tsx
) - Bottom Navigation (
src/components/subpage/RecommendationPanel.tsx
) - Recommendation carousels (
src/components/subpage/RecommendationPanel.tsx
)
- Utility trend bar chart(daily/weekly/monthly) (
See the open issues for a full list of proposed features (and known issues).
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
Distributed under the MIT License. See LICENSE.txt
for more information.
Team HomeE
Developer: Nancy Wan - [email protected]
Project Link: https://github.com/nancywan1004/home-e
Great acknowledgements to faculty, C16 fellow students and community members of the MDM program at Centre for Digital Media for all the supports and feedback.