Add a rules engine so I can be notified when I forget to close my garage door.

This commit is contained in:
MarkBryanMilligan
2021-07-15 23:34:15 -05:00
parent de50645a2c
commit 3d5cd6500f
81 changed files with 2044 additions and 231 deletions

View File

@@ -0,0 +1,78 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lanternsoftware.rules</groupId>
<artifactId>lantern-dataaccess-rules</artifactId>
<packaging>jar</packaging>
<version>1.0.0</version>
<name>lantern-dataaccess-rules</name>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.lanternsoftware.rules</groupId>
<artifactId>lantern-datamodel-rules</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-dao-mongo</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<executions>
<execution>
<goals>
<goal>testCompile</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
<configuration>
<optimize>true</optimize>
<showDeprecation>true</showDeprecation>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<configuration>
<archive>
<index>true</index>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,70 @@
package com.lanternsoftware.dataaccess.rules;
import com.lanternsoftware.datamodel.rules.Event;
import com.lanternsoftware.datamodel.rules.EventType;
import com.lanternsoftware.datamodel.rules.FcmDevice;
import com.lanternsoftware.datamodel.rules.Rule;
import com.lanternsoftware.util.dao.DaoQuery;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.DaoSort;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import com.lanternsoftware.util.dao.mongo.MongoProxy;
import java.util.Date;
import java.util.List;
public class MongoRulesDataAccess implements RulesDataAccess {
private final MongoProxy proxy;
public MongoRulesDataAccess(MongoConfig _config) {
proxy = new MongoProxy(_config);
proxy.ensureIndex(Rule.class, DaoSort.sort("account_id"));
proxy.ensureIndex(Event.class, DaoSort.sort("account_id").then("type").then("source_id").then("time"));
proxy.ensureIndex(FcmDevice.class, DaoSort.sort("account_id"));
}
@Override
public void shutdown() {
proxy.shutdown();
}
@Override
public void putRule(Rule _rule) {
proxy.save(_rule);
}
@Override
public List<Rule> getRulesForAccount(int _accountId) {
return proxy.query(Rule.class, new DaoQuery("account_id", _accountId));
}
@Override
public void deleteRule(String _ruleId) {
proxy.delete(Rule.class, new DaoQuery("_id", _ruleId));
}
@Override
public void putEvent(Event _event) {
proxy.save(_event);
}
@Override
public Event getMostRecentEvent(int _accountId, EventType _type, String _sourceId) {
return proxy.queryOne(Event.class, new DaoQuery("account_id", _accountId).and("type", DaoSerializer.toEnumName(_type)).and("source_id", _sourceId), DaoSort.sortDesc("time"));
}
@Override
public List<Event> getEvents(int _accountId, EventType _type, String _sourceId, Date _from, Date _to) {
return proxy.query(Event.class, new DaoQuery("account_id", _accountId).and("type", DaoSerializer.toEnumName(_type)).and("source_id", _sourceId).andBetweenInclusiveExclusive("time", DaoSerializer.toLong(_from), DaoSerializer.toLong(_to)));
}
@Override
public void putFcmDevice(FcmDevice _device) {
proxy.save(_device);
}
@Override
public List<FcmDevice> getFcmDevicesForAccount(int _accountId) {
return proxy.query(FcmDevice.class, new DaoQuery("account_id", _accountId));
}
}

View File

@@ -0,0 +1,21 @@
package com.lanternsoftware.dataaccess.rules;
import com.lanternsoftware.datamodel.rules.Event;
import com.lanternsoftware.datamodel.rules.EventType;
import com.lanternsoftware.datamodel.rules.FcmDevice;
import com.lanternsoftware.datamodel.rules.Rule;
import java.util.Date;
import java.util.List;
public interface RulesDataAccess {
void shutdown();
void putRule(Rule _rule);
List<Rule> getRulesForAccount(int _accountId);
void deleteRule(String _ruleId);
void putEvent(Event _event);
Event getMostRecentEvent(int _accountId, EventType _type, String _sourceId);
List<Event> getEvents(int _accountId, EventType _type, String _sourceId, Date _from, Date _to);
void putFcmDevice(FcmDevice _device);
List<FcmDevice> getFcmDevicesForAccount(int _accountId);
}

View File

@@ -0,0 +1,73 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lanternsoftware.rules</groupId>
<artifactId>lantern-datamodel-rules</artifactId>
<packaging>jar</packaging>
<version>1.0.0</version>
<name>lantern-datamodel-rules</name>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-dao</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<executions>
<execution>
<goals>
<goal>testCompile</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
<configuration>
<optimize>true</optimize>
<showDeprecation>true</showDeprecation>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<configuration>
<archive>
<index>true</index>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,43 @@
package com.lanternsoftware.datamodel.rules;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
@DBSerializable
public class Action {
private ActionType type;
private String description;
private String destinationId;
private double value;
public ActionType getType() {
return type;
}
public void setType(ActionType _type) {
type = _type;
}
public String getDescription() {
return description;
}
public void setDescription(String _description) {
description = _description;
}
public String getDestinationId() {
return destinationId;
}
public void setDestinationId(String _destinationId) {
destinationId = _destinationId;
}
public double getValue() {
return value;
}
public void setValue(double _value) {
value = _value;
}
}

View File

@@ -0,0 +1,7 @@
package com.lanternsoftware.datamodel.rules;
public enum ActionType {
SET_SWITCH,
MOBILE_ALERT_STATIC,
MOBILE_ALERT_EVENT_DESCRIPTION
}

View File

@@ -0,0 +1,23 @@
package com.lanternsoftware.datamodel.rules;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
@DBSerializable
public class Alert {
private String message;
public Alert() {
}
public Alert(String _message) {
message = _message;
}
public String getMessage() {
return message;
}
public void setMessage(String _message) {
message = _message;
}
}

View File

@@ -0,0 +1,172 @@
package com.lanternsoftware.datamodel.rules;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
@DBSerializable
public class Criteria {
private EventType type;
private String sourceId;
private Operator operator;
private double value;
private boolean or;
private List<Criteria> criteria;
public EventType getType() {
return type;
}
public void setType(EventType _type) {
type = _type;
}
public String getSourceId() {
return sourceId;
}
public void setSourceId(String _sourceId) {
sourceId = _sourceId;
}
public Operator getOperator() {
return operator;
}
public void setOperator(Operator _operator) {
operator = _operator;
}
public double getValue() {
return value;
}
public void setValue(double _value) {
value = _value;
}
public boolean isOr() {
return or;
}
public void setOr(boolean _or) {
or = _or;
}
public List<Criteria> getCriteria() {
return criteria;
}
public void setCriteria(List<Criteria> _criteria) {
criteria = _criteria;
}
public EventId toEventId() {
return new EventId(type, sourceId);
}
public Set<Integer> toJavaDays() {
if (type != EventType.TIME)
return Collections.emptySet();
return CollectionUtils.transformToSet(CriteriaDay.toEnumSet(sourceId), _d->_d.javaDay);
}
public boolean isMet(List<Event> _events, TimeZone _tz) {
Event e = CollectionUtils.filterOne(_events, this::triggers);
if (type == EventType.TIME) {
int day = DateUtils.toCalendar(new Date(), _tz).get(Calendar.DAY_OF_WEEK);
if (!toJavaDays().contains(day))
return false;
Date timeToday = timeOfDay(new Date(), DaoSerializer.toInteger(getValue()), day, _tz);
if (!e.getTime().equals(timeToday))
return false;
}
else if (operator != null) {
if (operator == Operator.GREATER) {
if (e.getValue() <= value)
return false;
}
else if (operator == Operator.GREATER_EQUAL) {
if (e.getValue() < value)
return false;
}
else if (operator == Operator.EQUAL) {
if (e.getValue() != value)
return false;
}
else if (operator == Operator.LESS_EQUAL) {
if (e.getValue() > value)
return false;
}
else if (operator == Operator.LESS) {
if (e.getValue() >= value)
return false;
}
}
if (CollectionUtils.isNotEmpty(criteria)) {
if (or)
return CollectionUtils.anyQualify(criteria, _c->_c.isMet(_events, _tz));
return CollectionUtils.allQualify(criteria, _c->_c.isMet(_events, _tz));
}
return true;
}
public boolean triggers(Event _event) {
if (_event.getType() != type)
return false;
if (NullUtils.isEmpty(_event.getSourceId()) || NullUtils.isEqual(_event.getSourceId(), "*") || NullUtils.isEqual(_event.getSourceId(), sourceId))
return true;
return CollectionUtils.anyQualify(criteria, _c->_c.triggers(_event));
}
public void addAllCriteria(List<Criteria> _criteria) {
_criteria.add(this);
CollectionUtils.edit(criteria, _c->_c.addAllCriteria(_criteria));
}
public Date getNextTriggerDate(TimeZone _tz) {
if (type != EventType.TIME)
return null;
Collection<Date> dates = CollectionUtils.transform(CriteriaDay.toEnumSet(getSourceId()), _s->nextTimeOfDay(new Date(), DaoSerializer.toInteger(getValue()), _s.javaDay, _tz));
return CollectionUtils.getSmallest(dates);
}
public Date timeOfDay(Date _now, int _time, int _day, TimeZone _tz) {
Calendar cal = DateUtils.toCalendar(_now, _tz);
cal.set(Calendar.DAY_OF_WEEK, _day);
cal.set(Calendar.HOUR_OF_DAY, hour(_time));
cal.set(Calendar.MINUTE, minute(_time));
cal.set(Calendar.SECOND, second(_time));
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
public Date nextTimeOfDay(Date _now, int _time, int _day, TimeZone _tz) {
Date time = timeOfDay(_now, _time, _day, _tz);
return time.before(_now)?DateUtils.addDays(time,7, _tz):time;
}
public int hour(int _timeOfDay) {
return _timeOfDay/3600;
}
public int minute(int _timeOfDay) {
return (_timeOfDay/60)%60;
}
public int second(int _timeOfDay) {
return _timeOfDay%60;
}
}

View File

@@ -0,0 +1,35 @@
package com.lanternsoftware.datamodel.rules;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import java.util.Calendar;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;
public enum CriteriaDay {
SUN(Calendar.SUNDAY),
MON(Calendar.MONDAY),
TUE(Calendar.TUESDAY),
WED(Calendar.WEDNESDAY),
THU(Calendar.THURSDAY),
FRI(Calendar.FRIDAY),
SAT(Calendar.SATURDAY);
public final int javaDay;
CriteriaDay(int _javaDay) {
javaDay = _javaDay;
}
public static String toString(Collection<CriteriaDay> _coll) {
return CollectionUtils.transformToCommaSeparated(_coll, Enum::name, false);
}
public static EnumSet<CriteriaDay> toEnumSet(String _days) {
String[] days = NullUtils.cleanSplit(_days, ",");
Set<CriteriaDay> setDays = CollectionUtils.transformToSet(CollectionUtils.asArrayList(days), _s->NullUtils.toEnum(CriteriaDay.class, _s));
return setDays.isEmpty()?EnumSet.allOf(CriteriaDay.class):EnumSet.copyOf(setDays);
}
}

View File

@@ -0,0 +1,73 @@
package com.lanternsoftware.datamodel.rules;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import com.lanternsoftware.util.dao.annotations.PrimaryKey;
import java.util.Date;
@DBSerializable
public class Event {
@PrimaryKey private String id;
private int accountId;
private EventType type;
private Date time;
private String eventDescription;
private String sourceId;
private double value;
public String getId() {
return id;
}
public void setId(String _id) {
id = _id;
}
public int getAccountId() {
return accountId;
}
public void setAccountId(int _accountId) {
accountId = _accountId;
}
public EventType getType() {
return type;
}
public void setType(EventType _type) {
type = _type;
}
public Date getTime() {
return time;
}
public void setTime(Date _time) {
time = _time;
}
public String getEventDescription() {
return eventDescription;
}
public void setEventDescription(String _eventDescription) {
eventDescription = _eventDescription;
}
public String getSourceId() {
return sourceId;
}
public void setSourceId(String _sourceId) {
sourceId = _sourceId;
}
public double getValue() {
return value;
}
public void setValue(double _value) {
value = _value;
}
}

View File

@@ -0,0 +1,34 @@
package com.lanternsoftware.datamodel.rules;
import java.util.Objects;
public class EventId {
private final EventType type;
private final String sourceId;
public EventId(EventType _type, String _sourceId) {
type = _type;
sourceId = _sourceId;
}
public EventType getType() {
return type;
}
public String getSourceId() {
return sourceId;
}
@Override
public boolean equals(Object _o) {
if (this == _o) return true;
if (_o == null || getClass() != _o.getClass()) return false;
EventId eventId = (EventId) _o;
return type == eventId.type && Objects.equals(sourceId, eventId.sourceId);
}
@Override
public int hashCode() {
return Objects.hash(type, sourceId);
}
}

View File

@@ -0,0 +1,8 @@
package com.lanternsoftware.datamodel.rules;
public enum EventType {
SWITCH_LEVEL,
TEMPERATURE,
TIME,
POWER
}

View File

@@ -0,0 +1,65 @@
package com.lanternsoftware.datamodel.rules;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import com.lanternsoftware.util.dao.annotations.PrimaryKey;
import java.util.Date;
@DBSerializable
public class FcmDevice {
@PrimaryKey private String id;
private int accountId;
private String token;
private String name;
private Date posted;
public FcmDevice() {
}
public FcmDevice(int _accountId, String _token, String _name, Date _posted) {
accountId = _accountId;
token = _token;
name = _name;
posted = _posted;
}
public String getId() {
return id;
}
public void setId(String _id) {
id = _id;
}
public int getAccountId() {
return accountId;
}
public void setAccountId(int _accountId) {
accountId = _accountId;
}
public String getToken() {
return token;
}
public void setToken(String _token) {
token = _token;
}
public String getName() {
return name;
}
public void setName(String _name) {
name = _name;
}
public Date getPosted() {
return posted;
}
public void setPosted(Date _posted) {
posted = _posted;
}
}

View File

@@ -0,0 +1,9 @@
package com.lanternsoftware.datamodel.rules;
public enum Operator {
GREATER,
GREATER_EQUAL,
EQUAL,
LESS_EQUAL,
LESS
}

View File

@@ -0,0 +1,80 @@
package com.lanternsoftware.datamodel.rules;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import com.lanternsoftware.util.dao.annotations.PrimaryKey;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
@DBSerializable
public class Rule {
@PrimaryKey private String id;
private int accountId;
private boolean or;
private List<Criteria> criteria;
private List<Action> actions;
public String getId() {
return id;
}
public void setId(String _id) {
id = _id;
}
public int getAccountId() {
return accountId;
}
public void setAccountId(int _accountId) {
accountId = _accountId;
}
public boolean isOr() {
return or;
}
public void setOr(boolean _or) {
or = _or;
}
public List<Criteria> getCriteria() {
return criteria;
}
public List<Criteria> getAllCriteria() {
List<Criteria> allCriteria = new ArrayList<>();
CollectionUtils.edit(criteria, _c->_c.addAllCriteria(allCriteria));
return allCriteria;
}
public void setCriteria(List<Criteria> _criteria) {
criteria = _criteria;
}
public List<Action> getActions() {
return actions;
}
public void setActions(List<Action> _actions) {
actions = _actions;
}
public boolean isMet(List<Event> _events, TimeZone _tz) {
if (or)
return CollectionUtils.anyQualify(criteria, _c->_c.isMet(_events, _tz));
return CollectionUtils.allQualify(criteria, _c->_c.isMet(_events, _tz));
}
public List<Criteria> getCriteriaNeedingData(Event _event) {
List<Criteria> allCriteria = getAllCriteria();
allCriteria.removeIf(_c->_c.triggers(_event));
return allCriteria;
}
public boolean triggers(Event _event) {
return CollectionUtils.anyQualify(criteria, _c-> _c.triggers(_event));
}
}

View File

@@ -0,0 +1,46 @@
package com.lanternsoftware.datamodel.rules.dao;
import com.lanternsoftware.datamodel.rules.Action;
import com.lanternsoftware.datamodel.rules.ActionType;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
public class ActionSerializer extends AbstractDaoSerializer<Action>
{
@Override
public Class<Action> getSupportedClass()
{
return Action.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(Action _o)
{
DaoEntity d = new DaoEntity();
d.put("type", DaoSerializer.toEnumName(_o.getType()));
d.put("description", _o.getDescription());
d.put("destination_id", _o.getDestinationId());
d.put("value", _o.getValue());
return d;
}
@Override
public Action fromDaoEntity(DaoEntity _d)
{
Action o = new Action();
o.setType(DaoSerializer.getEnum(_d, "type", ActionType.class));
o.setDescription(DaoSerializer.getString(_d, "description"));
o.setDestinationId(DaoSerializer.getString(_d, "destination_id"));
o.setValue(DaoSerializer.getDouble(_d, "value"));
return o;
}
}

View File

@@ -0,0 +1,39 @@
package com.lanternsoftware.datamodel.rules.dao;
import com.lanternsoftware.datamodel.rules.Alert;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
public class AlertSerializer extends AbstractDaoSerializer<Alert>
{
@Override
public Class<Alert> getSupportedClass()
{
return Alert.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(Alert _o)
{
DaoEntity d = new DaoEntity();
d.put("message", _o.getMessage());
return d;
}
@Override
public Alert fromDaoEntity(DaoEntity _d)
{
Alert o = new Alert();
o.setMessage(DaoSerializer.getString(_d, "message"));
return o;
}
}

View File

@@ -0,0 +1,51 @@
package com.lanternsoftware.datamodel.rules.dao;
import com.lanternsoftware.datamodel.rules.Criteria;
import com.lanternsoftware.datamodel.rules.EventType;
import com.lanternsoftware.datamodel.rules.Operator;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
public class CriteriaSerializer extends AbstractDaoSerializer<Criteria>
{
@Override
public Class<Criteria> getSupportedClass()
{
return Criteria.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(Criteria _o)
{
DaoEntity d = new DaoEntity();
d.put("type", DaoSerializer.toEnumName(_o.getType()));
d.put("source_id", _o.getSourceId());
d.put("operator", DaoSerializer.toEnumName(_o.getOperator()));
d.put("value", _o.getValue());
d.put("or", _o.isOr());
d.put("criteria", DaoSerializer.toDaoEntities(_o.getCriteria(), DaoProxyType.MONGO));
return d;
}
@Override
public Criteria fromDaoEntity(DaoEntity _d)
{
Criteria o = new Criteria();
o.setType(DaoSerializer.getEnum(_d, "type", EventType.class));
o.setSourceId(DaoSerializer.getString(_d, "source_id"));
o.setOperator(DaoSerializer.getEnum(_d, "operator", Operator.class));
o.setValue(DaoSerializer.getDouble(_d, "value"));
o.setOr(DaoSerializer.getBoolean(_d, "or"));
o.setCriteria(DaoSerializer.getList(_d, "criteria", Criteria.class));
return o;
}
}

View File

@@ -0,0 +1,53 @@
package com.lanternsoftware.datamodel.rules.dao;
import com.lanternsoftware.datamodel.rules.Event;
import com.lanternsoftware.datamodel.rules.EventType;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
public class EventSerializer extends AbstractDaoSerializer<Event>
{
@Override
public Class<Event> getSupportedClass()
{
return Event.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(Event _o)
{
DaoEntity d = new DaoEntity();
if (_o.getId() != null)
d.put("_id", _o.getId());
d.put("account_id", _o.getAccountId());
d.put("type", DaoSerializer.toEnumName(_o.getType()));
d.put("time", DaoSerializer.toLong(_o.getTime()));
d.put("event_description", _o.getEventDescription());
d.put("source_id", _o.getSourceId());
d.put("value", _o.getValue());
return d;
}
@Override
public Event fromDaoEntity(DaoEntity _d)
{
Event o = new Event();
o.setId(DaoSerializer.getString(_d, "_id"));
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
o.setType(DaoSerializer.getEnum(_d, "type", EventType.class));
o.setTime(DaoSerializer.getDate(_d, "time"));
o.setEventDescription(DaoSerializer.getString(_d, "event_description"));
o.setSourceId(DaoSerializer.getString(_d, "source_id"));
o.setValue(DaoSerializer.getDouble(_d, "value"));
return o;
}
}

View File

@@ -0,0 +1,48 @@
package com.lanternsoftware.datamodel.rules.dao;
import com.lanternsoftware.datamodel.rules.FcmDevice;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
public class FcmDeviceSerializer extends AbstractDaoSerializer<FcmDevice>
{
@Override
public Class<FcmDevice> getSupportedClass()
{
return FcmDevice.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(FcmDevice _o)
{
DaoEntity d = new DaoEntity();
if (_o.getId() != null)
d.put("_id", _o.getId());
d.put("account_id", _o.getAccountId());
d.put("token", _o.getToken());
d.put("name", _o.getName());
d.put("posted", DaoSerializer.toLong(_o.getPosted()));
return d;
}
@Override
public FcmDevice fromDaoEntity(DaoEntity _d)
{
FcmDevice o = new FcmDevice();
o.setId(DaoSerializer.getString(_d, "_id"));
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
o.setToken(DaoSerializer.getString(_d, "token"));
o.setName(DaoSerializer.getString(_d, "name"));
o.setPosted(DaoSerializer.getDate(_d, "posted"));
return o;
}
}

View File

@@ -0,0 +1,50 @@
package com.lanternsoftware.datamodel.rules.dao;
import com.lanternsoftware.datamodel.rules.Action;
import com.lanternsoftware.datamodel.rules.Criteria;
import com.lanternsoftware.datamodel.rules.Rule;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
public class RuleSerializer extends AbstractDaoSerializer<Rule>
{
@Override
public Class<Rule> getSupportedClass()
{
return Rule.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(Rule _o)
{
DaoEntity d = new DaoEntity();
if (_o.getId() != null)
d.put("_id", _o.getId());
d.put("account_id", _o.getAccountId());
d.put("or", _o.isOr());
d.put("criteria", DaoSerializer.toDaoEntities(_o.getCriteria(), DaoProxyType.MONGO));
d.put("actions", DaoSerializer.toDaoEntities(_o.getActions(), DaoProxyType.MONGO));
return d;
}
@Override
public Rule fromDaoEntity(DaoEntity _d)
{
Rule o = new Rule();
o.setId(DaoSerializer.getString(_d, "_id"));
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
o.setOr(DaoSerializer.getBoolean(_d, "or"));
o.setCriteria(DaoSerializer.getList(_d, "criteria", Criteria.class));
o.setActions(DaoSerializer.getList(_d, "actions", Action.class));
return o;
}
}

View File

@@ -0,0 +1,6 @@
com.lanternsoftware.datamodel.rules.dao.ActionSerializer
com.lanternsoftware.datamodel.rules.dao.AlertSerializer
com.lanternsoftware.datamodel.rules.dao.CriteriaSerializer
com.lanternsoftware.datamodel.rules.dao.EventSerializer
com.lanternsoftware.datamodel.rules.dao.FcmDeviceSerializer
com.lanternsoftware.datamodel.rules.dao.RuleSerializer

View File

@@ -0,0 +1,93 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lanternsoftware.rules</groupId>
<artifactId>lantern-service-rules</artifactId>
<packaging>jar</packaging>
<version>1.0.0</version>
<name>lantern-service-rules</name>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client-bom</artifactId>
<version>1.32.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
</dependency>
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>8.0.0</version>
</dependency>
<dependency>
<groupId>com.lanternsoftware.rules</groupId>
<artifactId>lantern-dataaccess-rules</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.lanternsoftware.currentmonitor</groupId>
<artifactId>lantern-dataaccess-currentmonitor</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-servlet</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<executions>
<execution>
<goals>
<goal>testCompile</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
<configuration>
<optimize>true</optimize>
<showDeprecation>true</showDeprecation>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,147 @@
package com.lanternsoftware.rules;
import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao;
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
import com.lanternsoftware.dataaccess.rules.MongoRulesDataAccess;
import com.lanternsoftware.dataaccess.rules.RulesDataAccess;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.datamodel.rules.Action;
import com.lanternsoftware.datamodel.rules.ActionType;
import com.lanternsoftware.datamodel.rules.Criteria;
import com.lanternsoftware.datamodel.rules.Event;
import com.lanternsoftware.datamodel.rules.EventId;
import com.lanternsoftware.datamodel.rules.EventType;
import com.lanternsoftware.datamodel.rules.Rule;
import com.lanternsoftware.rules.actions.ActionImpl;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class RulesEngine {
protected static final Logger LOG = LoggerFactory.getLogger(RulesEngine.class);
private static RulesEngine INSTANCE;
private final ExecutorService executor = Executors.newCachedThreadPool();
private final RulesDataAccess dao;
private final CurrentMonitorDao cmDao;
private final Map<ActionType, ActionImpl> actions = new HashMap<>();
private final Map<Integer, EventTimeTask> timeTasks = new HashMap<>();
private Timer timer;
public static RulesEngine instance() {
if (INSTANCE == null)
INSTANCE = new RulesEngine();
return INSTANCE;
}
public RulesEngine() {
ServiceLoader.load(ActionImpl.class).forEach(_action->actions.put(_action.getType(), _action));
dao = new MongoRulesDataAccess(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
cmDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
timer = new Timer("RulesEngine Timer");
}
public void start() {
for (String id : cmDao.getProxy().queryForField(Account.class, null, "_id")) {
scheduleNextTimeEventForAccount(DaoSerializer.toInteger(id));
}
}
public RulesDataAccess dao() {
return dao;
}
public void fireEvent(Event _event) {
if (_event.getType() != EventType.TIME)
dao.putEvent(_event);
executor.submit(()->{
TimeZone tz = TimeZone.getTimeZone("America/Chicago"); //TODO: Get from the current monitor account
List<Rule> rules = CollectionUtils.filter(dao.getRulesForAccount(_event.getAccountId()), _r->_r.triggers(_event));
if (!rules.isEmpty()) {
for (Rule rule : rules) {
List<Event> events = CollectionUtils.asArrayList(_event);
List<Criteria> critNeedingData = rule.getCriteriaNeedingData(_event);
if (!critNeedingData.isEmpty()) {
Set<EventId> eventsToGet = CollectionUtils.transformToSet(critNeedingData, Criteria::toEventId);
for (EventId id : eventsToGet) {
Event event = dao.getMostRecentEvent(_event.getAccountId(), id.getType(), id.getSourceId());
if (event != null)
events.add(event);
}
}
if (rule.isMet(events, tz)) {
for (Action action : CollectionUtils.makeNotNull(rule.getActions())) {
ActionImpl impl = actions.get(action.getType());
impl.invoke(rule, events, action);
}
}
}
}
});
}
private void scheduleNextTimeEventForAccount(int _accountId) {
TimeZone tz = TimeZone.getTimeZone("America/Chicago"); //TODO: Get from the current monitor account
EventTimeTask nextTask = timeTasks.remove(_accountId);
if (nextTask != null)
nextTask.cancel();
List<Rule> rules = CollectionUtils.filter(dao.getRulesForAccount(_accountId), _r->CollectionUtils.anyQualify(_r.getAllCriteria(), _c->_c.getType() == EventType.TIME));
if (rules.isEmpty())
return;
Collection<Date> dates = CollectionUtils.aggregate(rules, _r->CollectionUtils.transform(_r.getAllCriteria(), _c->_c.getNextTriggerDate(tz)));
Date nextDate = CollectionUtils.getSmallest(dates);
LOG.info("Scheduling next time event for account {} at {}", _accountId, DateUtils.format("MM/dd/yyyy HH:mm:ss", nextDate));
nextTask = new EventTimeTask(_accountId, nextDate);
timer.schedule(nextTask, nextDate);
}
public static void shutdown() {
if (INSTANCE == null)
return;
INSTANCE.executor.shutdown();
INSTANCE.dao.shutdown();
INSTANCE.cmDao.shutdown();
INSTANCE.timer.cancel();
INSTANCE.timer = null;
INSTANCE = null;
}
private class EventTimeTask extends TimerTask {
private final int accountId;
private final Date eventTime;
EventTimeTask(int _accountId, Date _eventTime) {
accountId = _accountId;
eventTime = _eventTime;
}
@Override
public void run() {
LOG.info("Firing time event for account {}", accountId);
Event event = new Event();
event.setAccountId(accountId);
event.setTime(eventTime);
event.setType(EventType.TIME);
fireEvent(event);
scheduleNextTimeEventForAccount(accountId);
}
}
}

View File

@@ -0,0 +1,52 @@
package com.lanternsoftware.rules.actions;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.AndroidConfig;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.Message;
import com.lanternsoftware.datamodel.rules.Alert;
import com.lanternsoftware.datamodel.rules.FcmDevice;
import com.lanternsoftware.datamodel.rules.Rule;
import com.lanternsoftware.rules.RulesEngine;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.dao.DaoSerializer;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
import java.util.List;
public abstract class AbstractAlertAction implements ActionImpl {
protected static final Logger logger = LoggerFactory.getLogger(AbstractAlertAction.class);
protected static final FirebaseMessaging messaging;
static {
FirebaseMessaging m = null;
try {
FileInputStream is = new FileInputStream(LanternFiles.OPS_PATH + "google_account_key.json");
FirebaseOptions options = FirebaseOptions.builder().setCredentials(GoogleCredentials.fromStream(is)).build();
m = FirebaseMessaging.getInstance(FirebaseApp.initializeApp(options));
IOUtils.closeQuietly(is);
}
catch (Exception _e) {
logger.error("Failed to load google credentials", _e);
}
messaging = m;
}
protected void sendAlert(Rule _rule, Alert _alert) {
List<FcmDevice> devices = RulesEngine.instance().dao().getFcmDevicesForAccount(_rule.getAccountId());
if (devices.isEmpty())
return;
for (FcmDevice device : devices) {
Message msg = Message.builder().setToken(device.getToken()).putData("payload", DaoSerializer.toBase64ZipBson(_alert)).putData("payloadClass", Alert.class.getCanonicalName()).setAndroidConfig(AndroidConfig.builder().setPriority(AndroidConfig.Priority.HIGH).setDirectBootOk(true).build()).build();
try {
messaging.send(msg);
} catch (Exception _e) {
logger.error("Failed to send message to account {}, device {}", _rule.getAccountId(), device.getName(), _e);
}
}
}
}

View File

@@ -0,0 +1,13 @@
package com.lanternsoftware.rules.actions;
import com.lanternsoftware.datamodel.rules.Action;
import com.lanternsoftware.datamodel.rules.ActionType;
import com.lanternsoftware.datamodel.rules.Event;
import com.lanternsoftware.datamodel.rules.Rule;
import java.util.List;
public interface ActionImpl {
ActionType getType();
void invoke(Rule _rule, List<Event> _event, Action _action);
}

View File

@@ -0,0 +1,22 @@
package com.lanternsoftware.rules.actions;
import com.lanternsoftware.datamodel.rules.Action;
import com.lanternsoftware.datamodel.rules.ActionType;
import com.lanternsoftware.datamodel.rules.Alert;
import com.lanternsoftware.datamodel.rules.Event;
import com.lanternsoftware.datamodel.rules.Rule;
import com.lanternsoftware.util.CollectionUtils;
import java.util.List;
public class MobileAlertAction extends AbstractAlertAction {
@Override
public ActionType getType() {
return ActionType.MOBILE_ALERT_EVENT_DESCRIPTION;
}
@Override
public void invoke(Rule _rule, List<Event> _event, Action _action) {
sendAlert(_rule, new Alert(CollectionUtils.transformToCommaSeparated(_event, Event::getEventDescription)));
}
}

View File

@@ -0,0 +1,22 @@
package com.lanternsoftware.rules.actions;
import com.lanternsoftware.datamodel.rules.Action;
import com.lanternsoftware.datamodel.rules.ActionType;
import com.lanternsoftware.datamodel.rules.Alert;
import com.lanternsoftware.datamodel.rules.Event;
import com.lanternsoftware.datamodel.rules.Rule;
import com.lanternsoftware.util.CollectionUtils;
import java.util.List;
public class MobileAlertStatic extends AbstractAlertAction {
@Override
public ActionType getType() {
return ActionType.MOBILE_ALERT_STATIC;
}
@Override
public void invoke(Rule _rule, List<Event> _event, Action _action) {
sendAlert(_rule, new Alert(_action.getDescription()));
}
}

View File

@@ -0,0 +1,24 @@
package com.lanternsoftware.rules.servlet;
import com.lanternsoftware.datamodel.rules.Event;
import com.lanternsoftware.rules.RulesEngine;
import com.lanternsoftware.util.dao.auth.AuthCode;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/event")
public class EventServlet extends SecureServlet {
@Override
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
Event event = getRequestPayload(_req, Event.class);
if (event == null) {
_rep.setStatus(400);
return;
}
event.setAccountId(_authCode.getAccountId());
RulesEngine.instance().fireEvent(event);
}
}

View File

@@ -0,0 +1,24 @@
package com.lanternsoftware.rules.servlet;
import com.lanternsoftware.datamodel.rules.FcmDevice;
import com.lanternsoftware.rules.RulesEngine;
import com.lanternsoftware.util.dao.auth.AuthCode;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/fcm")
public class FcmServlet extends SecureServlet {
@Override
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
FcmDevice device = getRequestPayload(_req, FcmDevice.class);
if (device == null) {
_rep.setStatus(400);
return;
}
device.setAccountId(_authCode.getAccountId());
RulesEngine.instance().dao().putFcmDevice(device);
}
}

View File

@@ -0,0 +1,39 @@
package com.lanternsoftware.rules.servlet;
import com.lanternsoftware.util.cryptography.AESTool;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.util.servlet.LanternServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public abstract class SecureServlet extends LanternServlet {
private static final AESTool aes = AESTool.authTool();
@Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) {
AuthCode authCode = DaoSerializer.fromZipBson(aes.decryptFromBase64(_req.getHeader("auth_code")), AuthCode.class);
if (authCode == null) {
_rep.setStatus(401);
return;
}
get(authCode, _req, _rep);
}
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
}
@Override
protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) {
AuthCode authCode = DaoSerializer.fromZipBson(aes.decryptFromBase64(_req.getHeader("auth_code")), AuthCode.class);
if (authCode == null) {
_rep.setStatus(401);
return;
}
post(authCode, _req, _rep);
}
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
}
}

View File

@@ -0,0 +1,2 @@
com.lanternsoftware.rules.actions.MobileAlertAction
com.lanternsoftware.rules.actions.MobileAlertStatic

View File

@@ -0,0 +1,165 @@
package com.lanternsoftware;
import com.lanternsoftware.datamodel.rules.Action;
import com.lanternsoftware.datamodel.rules.ActionType;
import com.lanternsoftware.datamodel.rules.Criteria;
import com.lanternsoftware.datamodel.rules.EventType;
import com.lanternsoftware.datamodel.rules.Operator;
import com.lanternsoftware.datamodel.rules.Rule;
import com.lanternsoftware.rules.RulesEngine;
import com.lanternsoftware.util.CollectionUtils;
public class CreateRules {
public static void main(String[] args) {
for (Rule r : RulesEngine.instance().dao().getRulesForAccount(100)) {
RulesEngine.instance().dao().deleteRule(r.getId());
}
Rule r1 = new Rule();
r1.setAccountId(100);
Criteria c1 = new Criteria();
c1.setType(EventType.SWITCH_LEVEL);
c1.setSourceId("203");
c1.setOperator(Operator.EQUAL);
c1.setValue(1);
r1.setCriteria(CollectionUtils.asArrayList(c1));
Action a1 = new Action();
a1.setType(ActionType.MOBILE_ALERT_STATIC);
a1.setDescription("Garage Door 1 opened");
a1.setDestinationId("*");
r1.setActions(CollectionUtils.asArrayList(a1));
RulesEngine.instance().dao().putRule(r1);
Rule r2 = new Rule();
r2.setAccountId(100);
Criteria c2 = new Criteria();
c2.setType(EventType.SWITCH_LEVEL);
c2.setSourceId("203");
c2.setOperator(Operator.EQUAL);
c2.setValue(0);
r2.setCriteria(CollectionUtils.asArrayList(c2));
Action a2 = new Action();
a2.setType(ActionType.MOBILE_ALERT_STATIC);
a2.setDescription("Garage Door 1 closed");
a2.setDestinationId("*");
r2.setActions(CollectionUtils.asArrayList(a2));
RulesEngine.instance().dao().putRule(r2);
Rule r3 = new Rule();
r3.setAccountId(100);
Criteria c3 = new Criteria();
c3.setType(EventType.SWITCH_LEVEL);
c3.setSourceId("204");
c3.setOperator(Operator.EQUAL);
c3.setValue(1);
r3.setCriteria(CollectionUtils.asArrayList(c3));
Action a3 = new Action();
a3.setType(ActionType.MOBILE_ALERT_STATIC);
a3.setDescription("Garage Door 2 opened");
a3.setDestinationId("*");
r3.setActions(CollectionUtils.asArrayList(a3));
RulesEngine.instance().dao().putRule(r3);
Rule r4 = new Rule();
r4.setAccountId(100);
Criteria c4 = new Criteria();
c4.setType(EventType.SWITCH_LEVEL);
c4.setSourceId("204");
c4.setOperator(Operator.EQUAL);
c4.setValue(0);
r4.setCriteria(CollectionUtils.asArrayList(c4));
Action a4 = new Action();
a4.setType(ActionType.MOBILE_ALERT_STATIC);
a4.setDescription("Garage Door 2 closed");
a4.setDestinationId("*");
r4.setActions(CollectionUtils.asArrayList(a4));
RulesEngine.instance().dao().putRule(r4);
Rule r5 = new Rule();
r5.setAccountId(100);
Criteria c5 = new Criteria();
c5.setType(EventType.SWITCH_LEVEL);
c5.setSourceId("205");
c5.setOperator(Operator.EQUAL);
c5.setValue(1);
r5.setCriteria(CollectionUtils.asArrayList(c5));
Action a5 = new Action();
a5.setType(ActionType.MOBILE_ALERT_STATIC);
a5.setDescription("Garage Door 3 opened");
a5.setDestinationId("*");
r5.setActions(CollectionUtils.asArrayList(a5));
RulesEngine.instance().dao().putRule(r5);
Rule r6 = new Rule();
r6.setAccountId(100);
Criteria c6 = new Criteria();
c6.setType(EventType.SWITCH_LEVEL);
c6.setSourceId("205");
c6.setOperator(Operator.EQUAL);
c6.setValue(0);
r6.setCriteria(CollectionUtils.asArrayList(c6));
Action a6 = new Action();
a6.setType(ActionType.MOBILE_ALERT_STATIC);
a6.setDescription("Garage Door 3 closed");
a6.setDestinationId("*");
r6.setActions(CollectionUtils.asArrayList(a6));
RulesEngine.instance().dao().putRule(r6);
Rule r7 = new Rule();
r7.setAccountId(100);
Criteria c7 = new Criteria();
c7.setType(EventType.SWITCH_LEVEL);
c7.setSourceId("203");
c7.setOperator(Operator.EQUAL);
c7.setValue(1);
Criteria c7_2 = new Criteria();
c7_2.setType(EventType.TIME);
c7_2.setValue(79200);
// c7_2.setValue(74400);
r7.setCriteria(CollectionUtils.asArrayList(c7, c7_2));
Action a7 = new Action();
a7.setType(ActionType.MOBILE_ALERT_STATIC);
a7.setDescription("Garage Door 1 is still open");
a7.setDestinationId("*");
r7.setActions(CollectionUtils.asArrayList(a7));
RulesEngine.instance().dao().putRule(r7);
Rule r8 = new Rule();
r8.setAccountId(100);
Criteria c8 = new Criteria();
c8.setType(EventType.SWITCH_LEVEL);
c8.setSourceId("204");
c8.setOperator(Operator.EQUAL);
c8.setValue(1);
Criteria c8_2 = new Criteria();
c8_2.setType(EventType.TIME);
c8_2.setValue(79200);
// c8_2.setValue(74400);
r8.setCriteria(CollectionUtils.asArrayList(c8, c8_2));
Action a8 = new Action();
a8.setType(ActionType.MOBILE_ALERT_STATIC);
a8.setDescription("Garage Door 2 is still open");
a8.setDestinationId("*");
r8.setActions(CollectionUtils.asArrayList(a8));
RulesEngine.instance().dao().putRule(r8);
Rule r9 = new Rule();
r9.setAccountId(100);
Criteria c9 = new Criteria();
c9.setType(EventType.SWITCH_LEVEL);
c9.setSourceId("205");
c9.setOperator(Operator.EQUAL);
c9.setValue(1);
Criteria c9_2 = new Criteria();
c9_2.setType(EventType.TIME);
c9_2.setValue(79200);
// c9_2.setValue(74400);
r9.setCriteria(CollectionUtils.asArrayList(c9, c9_2));
Action a9 = new Action();
a9.setType(ActionType.MOBILE_ALERT_STATIC);
a9.setDescription("Garage Door 3 is still open");
a9.setDestinationId("*");
r9.setActions(CollectionUtils.asArrayList(a9));
RulesEngine.instance().dao().putRule(r9);
RulesEngine.shutdown();
}
}

View File

@@ -0,0 +1,23 @@
package com.lanternsoftware;
import com.lanternsoftware.datamodel.rules.EventType;
import com.lanternsoftware.datamodel.rules.Rule;
import com.lanternsoftware.rules.RulesEngine;
import com.lanternsoftware.util.CollectionUtils;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
public class TestRuleSchedule {
public static void main(String[] args) {
TimeZone tz = TimeZone.getTimeZone("America/Chicago"); //TODO: Get from the current monitor account
List<Rule> rules = CollectionUtils.filter(RulesEngine.instance().dao().getRulesForAccount(100), _r->CollectionUtils.anyQualify(_r.getAllCriteria(), _c->_c.getType() == EventType.TIME));
if (rules.isEmpty())
return;
Collection<Date> dates = CollectionUtils.aggregate(rules, _r->CollectionUtils.transform(_r.getAllCriteria(), _c->_c.getNextTriggerDate(tz), true));
Date nextDate = CollectionUtils.getSmallest(dates);
}
}

View File

@@ -0,0 +1,36 @@
package com.lanternsoftware;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.Message;
import com.lanternsoftware.dataaccess.rules.MongoRulesDataAccess;
import com.lanternsoftware.dataaccess.rules.RulesDataAccess;
import com.lanternsoftware.datamodel.rules.Alert;
import com.lanternsoftware.datamodel.rules.FcmDevice;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import org.apache.commons.io.IOUtils;
import java.io.FileInputStream;
public class TestSendAlert {
public static void main(String[] args) {
RulesDataAccess dao = new MongoRulesDataAccess(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
for (FcmDevice d : dao.getFcmDevicesForAccount(100)) {
Alert alert = new Alert();
alert.setMessage("Test Alert");
Message msg = Message.builder().setToken(d.getToken()).putData("payload", DaoSerializer.toBase64ZipBson(alert)).putData("payloadClass", Alert.class.getCanonicalName()).build();
try {
FileInputStream is = new FileInputStream("d:\\zwave\\firebase\\account_key.json");
FirebaseOptions options = FirebaseOptions.builder().setCredentials(GoogleCredentials.fromStream(is)).build();
FirebaseMessaging.getInstance(FirebaseApp.initializeApp(options)).send(msg);
IOUtils.closeQuietly(is);
} catch (Exception _e) {
_e.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,21 @@
package com.lanternsoftware;
import com.lanternsoftware.datamodel.rules.Event;
import com.lanternsoftware.datamodel.rules.EventType;
import com.lanternsoftware.rules.RulesEngine;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
import java.util.TimeZone;
public class TestTimeRule {
public static void main(String[] args) {
Event event = new Event();
event.setAccountId(100);
event.setTime(DateUtils.date(7, 15, 2021, 18, 30, 0, 0, TimeZone.getTimeZone("America/Chicago")));
event.setType(EventType.TIME);
RulesEngine.instance().fireEvent(event);
ConcurrencyUtils.sleep(200000);
RulesEngine.shutdown();
}
}

19
rules/pom.xml Normal file
View File

@@ -0,0 +1,19 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<groupId>com.lanternsoftware.rules</groupId>
<artifactId>rules-reactor</artifactId>
<name>rules-reactor</name>
<version>1.0.0</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<modules>
<module>lantern-dataaccess-rules</module>
<module>lantern-datamodel-rules</module>
<module>lantern-service-rules</module>
</modules>
</project>