Skip to content

Commit

Permalink
add readme md
Browse files Browse the repository at this point in the history
  • Loading branch information
pyckle authored and Andrew Pikler committed Jun 13, 2024
1 parent 0fd2fec commit 168bea1
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 8 deletions.
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# oref-alerts

An Unofficial Java Library and Swing UI to programmically access and display Alerts from Israel's Homefront Command

## What is the purpose of this project?

* A Java library that reliably receives alerts from Pekudei Oref's unofficial REST APIs
- Programmatically merge output of multiple Oref REST APIs
- Automatically update information asynchronously to program execution
* A SwingUI that displays recent historical alerts and regularly updates with new alerts
* Lightweight enough to run on a Raspberry Pi 3A+ with 512 MB of RAM and render at 1080p

## Features

* Rich library with clean API to integration with Pekudei Oref's unofficial APIs
- Updates from Unofficial Pekudei Oref API regularly
- Refreshes historical API in the case of a network outage to prevent missed Alerts
- Fetches city and alert translations from Oref APIs
- Respects HTTP Caching Headers and adds in random delay to avoid server load after cache expiration
- Decodes alert time from Alert API id (which appears to use Microsoft's Filetime Epoch). Graceful fallback on
failure

* SwingUI to display alerts
- Designed to work well on wide screens, not narrow phones
- Shows last updated time from last-modified HTTP Response Header (rather than local or server timestamp)
- Supports Hebrew, English, Russian, and Arabic
- Minimalistic
- Very few dependencies
- JAR is <.5MB
- Runs fine with 32 MB Java Heap

## TODO

* More automated tests are necessary
* Support fullscreen
* Support multiple monitors
* Add visible warning if updates fail to fetch

## Build Instructions

### Requirements

Java 17+
A recent version of Maven

### Build Instructions

Checkout this repository<br />
run <pre>mvn clean install</pre>
A self executable jar will be built: <pre>./oref-swingui/target/oref-swingui.jar</pre>
Run it!
<pre>java -jar ./oref-swingui/target/oref-swingui.jar</pre>
If you want to change the language (Hebrew by default)
<pre>java -jar ./oref-swingui/target/oref-swingui.jar en</pre>
or make the font bigger
<pre>java -jar ./oref-swingui/target/oref-swingui.jar 20 en</pre>

## Disclaimers

This application is not intended as a substitute to adhering to Homefront Command's guidelines.
It utilizes unofficial APIs and should **not** be relied upon for receiving live saving notifications.
**Always** follow official lifesaving instructions and procedures.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

1 change: 1 addition & 0 deletions oref-integration/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<goal>shade</goal>
</goals>
<configuration>
<outputFile>${project.build.directory}/oref-integration-cli.jar</outputFile>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@
import com.github.pyckle.oref.alerts.categories.AlertCategories;
import com.github.pyckle.oref.alerts.categories.dto.AlertCategory;
import com.github.pyckle.oref.integration.activealerts.ActiveAlert;
import com.github.pyckle.oref.integration.activealerts.AlertsRolloverStorage;
import com.github.pyckle.oref.integration.activealerts.FileTimeToInstantUtil;
import com.github.pyckle.oref.integration.caching.OrefApiCachingService;
import com.github.pyckle.oref.integration.config.OrefConfig;
import com.github.pyckle.oref.integration.datetime.OrefDateTimeUtils;
import com.github.pyckle.oref.integration.translationstores.DistrictStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.time.LocalDateTime;
import java.util.List;

public class AlertDetailsFactory {
private static final Logger logger = LoggerFactory.getLogger(AlertDetailsFactory.class);

private final OrefConfig orefConfig;
private final OrefApiCachingService cachingService;

Expand All @@ -22,8 +28,7 @@ public AlertDetailsFactory(OrefConfig orefConfig, OrefApiCachingService cachingS
}

public AlertDetails buildAlertDetails(ActiveAlert alert) {
Instant decodedTimestamp = alert.alertTimestamps().getDecodedTimestamp();
LocalDateTime decodedDateTime = OrefDateTimeUtils.toLocalDateTime(decodedTimestamp);
LocalDateTime decodedDateTime = getRemoteTimestamp(alert);

List<String> translatedAreas = translateAreas(alert.filteredAreasToDisplay());
boolean isDrill = AlertCategories.INSTANCE.isDrill(alert.alertCategoryId());
Expand All @@ -33,6 +38,21 @@ public AlertDetails buildAlertDetails(ActiveAlert alert) {
translatedAreas, alert.filteredAreasToDisplay());
}

private static LocalDateTime getRemoteTimestamp(ActiveAlert alert) {
final Instant decodedTimestamp;

if (alert.alertTimestamps().successfullyDecodedTimestamp()) {
decodedTimestamp = alert.alertTimestamps().getDecodedTimestamp();
} else {
// In the event that we couldn't decode the remote timestamp for any reason, use the received timestamp
// We could use the last-modified or the server time http headers, but more plumbing is needed.
logger.debug("Unable to decode timestamp for alert {}. Falling back to received timestamp", alert);
decodedTimestamp = alert.alertTimestamps().getReceivedTimestamp();
}
LocalDateTime decodedDateTime = OrefDateTimeUtils.toLocalDateTime(decodedTimestamp);
return decodedDateTime;
}

private String translateCategory(String categoryId, String hebCat) {
String translatedCategory = cachingService.getAlertDescriptions().retrievedValue()
.getAlertStringFromMatId(categoryId, hebCat);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ private static String formatAsTimestamp(Instant toFormat) {
return OrefDateTimeUtils.toLocalTime(toFormat.truncatedTo(ChronoUnit.SECONDS)).toString();
}

/**
* @return whether the remote timestamp was successfully decoded.
*/
public boolean successfullyDecodedTimestamp() {
return !FileTimeToInstantUtil.UNKNOWN_DECODED_TIME.equals(decodedTimestamp);
}

/**
* @return the timestamp that was decoded from the {@link Alert#id()} field
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Converts file time to Instant
*/
public class FileTimeToInstantUtil {
public static final Instant UNKNOWN_DECODED_TIME = Instant.EPOCH;
private static final long FILETIME_EPOCH_DIFF = 11644473600L;


Expand All @@ -19,11 +20,11 @@ public static Instant fileTimeToInstant(String fileTime) {

// if we get something before the epoch, something went wrong.
if (Instant.EPOCH.isAfter(ret))
return Instant.EPOCH;
return UNKNOWN_DECODED_TIME;

return ret;
} catch (NumberFormatException e) {
return Instant.EPOCH;
return UNKNOWN_DECODED_TIME;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import java.util.Properties;
import java.util.Set;

/**
* Configuration of Pekudei Oref View
*/
public class OrefConfig {
public static final String ROOT_PROP = "oref";
private static final String DEFAULT_ENABLED = "true";
Expand Down
1 change: 1 addition & 0 deletions oref-swingui/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<goal>shade</goal>
</goals>
<configuration>
<outputFile>${project.build.directory}/oref-swingui.jar</outputFile>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
Expand Down
25 changes: 24 additions & 1 deletion oref-swingui/src/main/java/com/github/pyckle/oref/ui/Main.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
package com.github.pyckle.oref.ui;

import java.util.Arrays;
import java.util.Properties;
import java.util.regex.Pattern;

public class Main {

public static void main(String[] args) {
configSlf4j(args);
javax.swing.SwingUtilities.invokeLater(() -> new PekudeiOrefGui().createAndShowGUI());
Properties properties = parseProperties(args);
javax.swing.SwingUtilities.invokeLater(() -> new PekudeiOrefGui(properties).createAndShowGUI());
}

private static Properties parseProperties(String[] args) {
Properties properties = new Properties();
Pattern pattern = Pattern.compile("^\\d+$");
for (String arg : args) {
switch (arg) {
case "en":
case "ru":
case "ar":
case "he":
properties.setProperty("oref.lang", arg);
break;
}
if (pattern.matcher(arg).matches()) {
properties.setProperty("oref.font_size", arg);
}
}
return properties;
}

private static void configSlf4j(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.time.Instant;
import java.util.Properties;

public class PekudeiOrefGui {
private final Properties properties;

public PekudeiOrefGui(Properties properties) {
this.properties = properties;
}

void createAndShowGUI() {
Properties properties = new Properties();
properties.setProperty(OrefConfig.ROOT_PROP + "." + OrefConfig.LANG, "he");
PekudeiOrefView orefView = new PekudeiOrefView(new OrefConfig(properties));

JFrame frame = new JFrame("Oref Dashboard");
Expand Down

0 comments on commit 168bea1

Please sign in to comment.