Skip to content

Commit

Permalink
v0.0.1-beta
Browse files Browse the repository at this point in the history
  • Loading branch information
IzakJoubert committed Aug 1, 2023
1 parent f22c5f7 commit 2cb57ba
Show file tree
Hide file tree
Showing 53 changed files with 4,820 additions and 3,142 deletions.
Binary file added .github/assets/step1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/step2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/step3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/step4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/step5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/step6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/step7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/assets/tray.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dist
packages
.webpack
out
yarn-error.log
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @zjouba
15 changes: 15 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Contributing

We really appreciate any contributions that help improve Shed Shield. All contributions are welcome, including feature requests, issues, documentation, and discussions.

## Got a question?

You can ask questions, see announcements, and discuss Shed Shield related topics on the [Discussion page](https://github.com/ZJouba/ShedShield/discussions).

## Found a bug?

If you find any bugs please help us by [submitting an issue](https://github.com/ZJouba/ShedShield/issues/new?labels=bug) to our GitHub Repository.

## Missing a feature?

You can request a new feature by [submitting an issue](https://github.com/ZJouba/ShedShield/issues/new?labels=enhancement) to our GitHub Repository.
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

97 changes: 76 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,90 @@
## Electron + TypeScript + React
<h1 align='center'>
<br>
<img src='./assets/icon.png' alt='Shed Shield' width=200>
<br>
Shed Shield
<br>
</h1>

Boilerplate for a project using Electron, React and Typescript.
<h4 align='center'>A handy little utility to shutdown your PC before loadingshedding hits. Built with <a href="https://www.electronjs.org/" target="_blank">Electron</a>.</h4>

## Installation
<div align='center'>
<a href='https://github.com/zjouba/shedshield/releases'>

<img src='https://img.shields.io/github/v/release/zjouba/shedshield?color=%23FDD835&label=version&style=for-the-badge'>

</a>

Use a package manager of your choice (npm, yarn, etc.) in order to install all dependencies
<a href='https://github.com/zjouba/shedshield/blob/master/LICENSE'>

<img src='https://img.shields.io/github/license/zjouba/shedshield?style=for-the-badge'>

</a>
</div>
<br />

```bash
yarn
```
---

## Usage
<div align='center'>

Just run `start` script.
**[SHED SHIELD](#🛡️shed-shield)
[KEY FEATURES](#🔧-key-features)
[FIRST TIME SETUP](#⌨️-first-time-setup)
[DOWNLOAD](#💾-download)
[CONTRIBUTING](#🦾-contributing)
[SUPPORT](#🙏support)
[LICENSE](#📜-license)**

```bash
yarn start
```
</div>

## Packaging
# 🛡️Shed Shield
Shed Shield uses the [EskomSePush](https://documenter.getpostman.com/view/1296288/UzQuNk3E#intro) API along with [Nominatum](https://nominatim.openstreetmap.org/ui/about.html) to look up your zone and adds a cron job to shutdown your PC before loadshedding cuts the power.

To generate the project package based on the OS you're running on, just run:
# 🔧 Key Features
* Shed Shield uses geolocation to find your closest zone
* Shed Shield can monitor multiple zones
* Shed Shield uses the most immediate loadshedding timeslot to schedule a shutdown
* Shed Shield can be configured to shutdown at specified intervals before loadshedding

```bash
yarn package
```
# ⌨️ First time setup
### 1. EskomSePush API
a. Before running Shed Shield, navigate to [EskomSePush API Subscription](https://eskomsepush.gumroad.com/l/api) and subscribe to the free tier
<img src='.github/assets/step1.png' alt='Shed Shield' width=500>
b. Complete your details
<img src='.github/assets/step2.png' alt='Shed Shield' width=500>
c. Use the provided API key when setting up Shed Shield

## Contributing
### 2. Shed Shield
When running Shed Shield for the first time, you first have to setup your API Key and then your zones.
a. Run Shed Shield
<img src='.github/assets/step3.png' alt='Shed Shield' width=500>
b. Follow the instructions and go to the Settings tab
<img src='.github/assets/step4.png' alt='Shed Shield' width=500>
c. Enter the API Key from [Step 1](#1-eskomsepush-api) into the input field
d. Click the Save Settings button
e. Search for your street address or nearest location using the Address lookup
<img src='.github/assets/step5.png' alt='Shed Shield' width=500>
f. Click SEARCH FOR YOUR ZONE to search for your closest Zone
<b>NB! Don't search for too many zones, this request uses 5 units of your API quota with EskomSePush!</b>
g. Select the zones you want to monitor, the interval and be sure to check Launch at startup to have worry free shutdowns
<img src='.github/assets/step6.png' alt='Shed Shield' width=500>
h. And that's all there is too it
<img src='.github/assets/step7.png' alt='Shed Shield' width=500>

Pull requests are always welcome 😃.
You can also see at what time the app will shutdown in the system tray
<img src='.github/assets/tray.png' alt='Shed Shield' width=200>

## License
<h3>Be sure to keep the app running in order to schedule the shutdowns. It will minimize to the system tray</h2>
<br>

[MIT](https://choosealicense.com/licenses/mit/)
# 💾 Download
You can [download](https://github.com/zjouba/shedshield/releases/tag/latest) the Shed Shield installer for Windows. (macOS & Linux are WIP)

# 🦾 Contributing
Shed Shield is an open-source project. We appreciate the community's involvement and feedback. Please refer to our [contribution](https://github.com/zjouba/shedshield/blob/master/contributing.md) guide for more information.

# 🙏Support
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/B0B5NRT22)

# 📜 License
Shed Shield is free and open-source software licensed under the [GNU General Public License v3.0.](https://github.com/zjouba/shedshield/blob/master/license)
Empty file removed assets/.gitkeep
Empty file.
Binary file added assets/icon.ico
Binary file not shown.
Binary file added assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions electron/api/esp.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Store from 'electron-store'
import ISettings from '../../src/interfaces/ISettings';
const ESP = 'https://developer.sepush.co.za/business/2.0/'

const store = new Store()

const ESPToken = () => {
return {
headers: {
'Token': (store.get('settings') as ISettings)?.apiKey
}
}
};

export const espAPI = {
searchArea: (searchQuery: string): Promise<Response> => fetch(`${ESP}areas_search?${searchQuery}`, {method: 'GET', ...ESPToken()}),

areaInfo: (id: string): Promise<Response> => fetch(`${ESP}area?id=${id}${process.env.NODE_ENV !== 'production' ? '&test=current' : ''}`, {method: 'GET', ...ESPToken()}),
}
5 changes: 5 additions & 0 deletions electron/api/geolocate.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const NOMINATIM = 'https://nominatim.openstreetmap.org/'

export const geolocateAPI = {
search: (searchQuery: string): Promise<Response> => fetch(`${NOMINATIM}search?format=json&countrycodes=za&q=${searchQuery}`, {method: 'GET'})
}
58 changes: 44 additions & 14 deletions electron/bridge.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,54 @@
import { contextBridge, ipcRenderer } from 'electron'
import { contextBridge, ipcRenderer } from 'electron';
import ISettings from '../src/interfaces/ISettings';
import { Dayjs } from 'dayjs';

export const api = {
/**
* Here you can expose functions to the renderer process
* so they can interact with the main (electron) side
* without security problems.
*
* The function below can accessed using `window.Main.sendMessage`
*/
const api = {
getSettings: async () => {
return await ipcRenderer.invoke('getSettings');
},

saveSettings: async (settings: ISettings) => {
return await ipcRenderer.invoke('saveSettings', settings);
},

searchArea: async (searchQuery: string) => {
return await ipcRenderer.invoke('searchArea', searchQuery);
},

geolocate: async (searchQuery: string) => {
return await ipcRenderer.invoke('geolocate', searchQuery);
},

getAreaInfo: async (id: string) => {
return await ipcRenderer.invoke('getAreaInfo', id);
},

sendMessage: (message: string) => {
ipcRenderer.send('message', message)
setCron: async (cron: Date, formatted: string) => {
return await ipcRenderer.invoke('setCron', cron, formatted);
},

info: async (...args: any[]) => {
ipcRenderer.send('info', args);
},

warn: async (...args: any[]) => {
ipcRenderer.send('warn', args);
},

error: async (...args: any[]) => {
ipcRenderer.send('error', args);
},

/**
* Provide an easier way to listen to events
*/
on: (channel: string, callback: Function) => {
ipcRenderer.on(channel, (_, data) => callback(data))
}
}

contextBridge.exposeInMainWorld('Main', api)

const theme = {
set: async (theme: string) => {
ipcRenderer.invoke('setTheme', theme);
}
}
contextBridge.exposeInMainWorld('theme', theme);
83 changes: 83 additions & 0 deletions electron/main.handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { IpcMain, Tray, app, nativeTheme } from 'electron'
import { geolocateAPI } from './api/geolocate.api'
import { espAPI } from './api/esp.api'
import Store from 'electron-store'
import ISettings from '../src/interfaces/ISettings'
import { CronJob } from 'cron'
import { shutdown } from './utils/shutdown'
import logger from './utils/logger'
import { Dayjs } from 'dayjs'

const store = new Store()

export const registerHandlers = (ipcMain: IpcMain, tray: Tray | null) => {
let job: CronJob

ipcMain.handle('getSettings', () => {
const settings: ISettings = store.get('settings') as ISettings || {
espAreas: [],
theme: '',
interval: 0,
runAtStartup: false,
};

if (!settings.theme)
settings.theme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light'
return settings
});

ipcMain.handle('saveSettings', (event: any, settings: ISettings) => {
store.set('settings', settings);
app.setLoginItemSettings({
openAtLogin: settings.runAtStartup
});
});

ipcMain.handle('setTheme', (event: any, theme: string) => {
nativeTheme.themeSource = theme as ('system' | 'light' | 'dark');
});

ipcMain.handle('searchArea', async (event: any, searchQuery: string) => {
try {
const response = await espAPI.searchArea(searchQuery);
const results = await response.json();
return results?.areas;
} catch (error) {
logger.error(error)
return [];
}
});

ipcMain.handle('geolocate', async (event: any, searchQuery: string) => {
const response = await geolocateAPI.search(searchQuery)
return await response.json()
});

ipcMain.handle('getAreaInfo', async (event: any, id: string) => {
const response = await espAPI.areaInfo(id)
return await response.json()
});

ipcMain.handle('setCron', async (event: any, cron: Date, formatted: string) => {
if (job) {
job.stop()
}
try {
job = new CronJob(
cron,
() => {
shutdown();
},
null,
true,
);
tray?.setToolTip(`Shutting down at ${formatted}`);
} catch (e) {
logger.error(e);
}
});

ipcMain.handle('info', async (event: any, ...args: any[]) => logger.info(args));
ipcMain.handle('error', async (event: any, ...args: any[]) => logger.error(args));
ipcMain.handle('warn', async (event: any, ...args: any[]) => logger.warn(args));
}
Loading

0 comments on commit 2cb57ba

Please sign in to comment.