/*
* Copyright 2012-2014 Nikolay A. Viguro
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ru.iris.devices.noolite;
import com.avaje.ebean.Ebean;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import ru.iris.common.database.model.SensorData;
import ru.iris.common.database.model.devices.Device;
import ru.iris.common.database.model.devices.DeviceValue;
import ru.iris.common.helpers.DBLogger;
import ru.iris.common.messaging.JsonMessaging;
import ru.iris.common.messaging.model.devices.GenericAdvertisement;
import ru.iris.common.modulestatus.Status;
import ru.iris.noolite4j.receiver.RX2164;
import ru.iris.noolite4j.watchers.BatteryState;
import ru.iris.noolite4j.watchers.Notification;
import ru.iris.noolite4j.watchers.SensorType;
import ru.iris.noolite4j.watchers.Watcher;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class NooliteRXService {
private final Logger LOGGER = LogManager.getLogger(NooliteRXService.class.getName());
private RX2164 rx;
private JsonMessaging messaging;
private InternalCommands internalCommands;
public NooliteRXService() {
Status status = new Status("Noolite-RX");
if (status.checkExist()) {
status.running();
} else {
status.addIntoDB("Noolite RX", "Service that check incoming Noolite commands");
}
internalCommands = new InternalCommands();
try {
messaging = new JsonMessaging(UUID.randomUUID(), "devices-noolite-rx");
rx = new RX2164();
rx.open();
Watcher watcher = new Watcher() {
@Override
public void onNotification(Notification notification) {
byte channel = notification.getChannel();
SensorType sensor = (SensorType) notification.getValue("sensortype");
Device device = loadByChannel(channel);
if (device == null) {
device = new Device();
device.setSource("noolite");
device.setInternalName("noolite/channel/" + channel);
device.setStatus("listening");
device.setType("Noolite Device");
device.setManufName("Nootechnika");
device.setNode((short) (1000 + channel));
device.setUuid(UUID.randomUUID().toString());
// device is not sensor
if (sensor == null) {
device.setInternalType("switch");
device.addValue(new DeviceValue("channel", String.valueOf(channel), "", "", device.getUuid(), true));
device.addValue(new DeviceValue("type", "switch", "", "", device.getUuid(), false));
} else {
device.setInternalType("sensor");
device.addValue(new DeviceValue("channel", String.valueOf(channel), "", "", device.getUuid(), true));
device.addValue(new DeviceValue("type", "sensor", "", "", device.getUuid(), false));
device.addValue(new DeviceValue("sensorname", sensor.name(), "", "", device.getUuid(), false));
}
}
Map<String, Object> params = new HashMap<>();
// turn off
switch (notification.getType()) {
case TURN_OFF:
LOGGER.info("Channel " + channel + ": Got OFF command");
updateValue(device, "Level", "0");
DBLogger.info("Device is OFF", device.getUuid());
SensorData.log(device.getUuid(), "Switch", "OFF");
// device product name unkown
if (device.getProductName().equals("unknown")) {
device.setProductName("Generic Switch");
}
messaging.broadcast("event.devices.noolite.value.changed", new GenericAdvertisement("DeviceOff", device.getUuid()));
break;
case SLOW_TURN_OFF:
LOGGER.info("Channel " + channel + ": Got DIM command");
// we only know, that the user hold OFF button
updateValue(device, "Level", "0");
DBLogger.info("Device is DIM", device.getUuid());
SensorData.log(device.getUuid(), "Switch", "DIM");
messaging.broadcast("event.devices.noolite.value.changed", new GenericAdvertisement("DeviceDim", device.getUuid()));
break;
case TURN_ON:
LOGGER.info("Channel " + channel + ": Got ON command");
updateValue(device, "Level", "255");
DBLogger.info("Device is ON", device.getUuid());
SensorData.log(device.getUuid(), "Switch", "ON");
// device product name unkown
if (device.getProductName().equals("unknown")) {
device.setProductName("Generic Switch");
}
messaging.broadcast("event.devices.noolite.value.changed", new GenericAdvertisement("DeviceOn", device.getUuid()));
break;
case SLOW_TURN_ON:
LOGGER.info("Channel " + channel + ": Got BRIGHT command");
// we only know, that the user hold ON button
updateValue(device, "Level", "255");
DBLogger.info("Device is BRIGHT", device.getUuid());
SensorData.log(device.getUuid(), "Switch", "BRIGHT");
messaging.broadcast("event.devices.noolite.value.changed", new GenericAdvertisement("DeviceBright", device.getUuid()));
break;
case SET_LEVEL:
LOGGER.info("Channel " + channel + ": Got SETLEVEL command.");
updateValue(device, "Level", (String) notification.getValue("level"));
DBLogger.info("Device get SETLEVEL: " + notification.getValue("level"), device.getUuid());
SensorData.log(device.getUuid(), "SetLevel", String.valueOf(notification.getValue("level")));
params.put("uuid", device.getUuid());
params.put("label", "Level");
params.put("data", notification.getValue("level"));
// device product name unkown
if (device.getProductName().equals("unknown") || device.getProductName().equals("Generic Switch")) {
device.setProductName("Generic Dimmer");
}
messaging.broadcast("event.devices.noolite.value.changed", new GenericAdvertisement("DeviceSetLevel", params));
break;
case STOP_DIM_BRIGHT:
LOGGER.info("Channel " + channel + ": Got STOPDIMBRIGHT command.");
DBLogger.info("Device is STOPDIMBRIGHT", device.getUuid());
SensorData.log(device.getUuid(), "Switch", "STOPDIMBRIGHT");
messaging.broadcast("event.devices.noolite.value.changed", new GenericAdvertisement("DeviceStopDimBright", device.getUuid()));
break;
case TEMP_HUMI:
BatteryState battery = (BatteryState) notification.getValue("battery");
LOGGER.info("Channel " + channel + ": Got TEMP_HUMI command.");
DBLogger.info("Device got TEMP_HUMI", device.getUuid());
updateValue(device, "Temperature", String.valueOf(notification.getValue("temp")));
SensorData.log(device.getUuid(), "Temperature", String.valueOf(notification.getValue("temp")));
DBLogger.info("Temperature is " + notification.getValue("temp"), device.getUuid());
updateValue(device, "Humidity", String.valueOf(notification.getValue("humi")));
SensorData.log(device.getUuid(), "Humidity", String.valueOf(notification.getValue("humi")));
DBLogger.info("Humidity is " + notification.getValue("humi"), device.getUuid());
updateValue(device, "Battery", battery.name());
SensorData.log(device.getUuid(), "Battery", String.valueOf(notification.getValue("battery")));
DBLogger.info("Battery is " + battery.name(), device.getUuid());
params.put("uuid", device.getUuid());
params.put("temp", notification.getValue("temp"));
params.put("humi", notification.getValue("humi"));
params.put("battery", battery.name());
// device product name unkown
if (device.getProductName().equals("unknown")) {
if ((int) notification.getValue("humi") == 0) {
device.setProductName("PT112");
} else {
device.setProductName("PT111");
}
}
messaging.broadcast("event.devices.noolite.value.changed", new GenericAdvertisement("DeviceTempHumi", params));
break;
case BATTERY_LOW:
LOGGER.info("Channel " + channel + ": Got BATTERYLOW command.");
DBLogger.info("Device battery low!", device.getUuid());
SensorData.log(device.getUuid(), "Battery", "BATTERYLOW");
// device product name unkown
if (device.getProductName().equals("Generic Switch")) {
device.setProductName("PM111");
device.setInternalType("sensor");
}
messaging.broadcast("event.devices.noolite.battery.replace", new GenericAdvertisement("DeviceBatteryLow", device.getUuid()));
break;
default:
LOGGER.info("Unknown command: " + notification.getType().name());
}
device.save();
}
};
rx.addWatcher(watcher);
rx.start();
} catch (Throwable t) {
LOGGER.error("Noolite RX error!");
status.crashed();
t.printStackTrace();
}
}
private Device loadByChannel(int channel) {
for (Device device : Ebean.find(Device.class).where().eq("source", "noolite").findList()) {
if (device.getInternalName().equals("noolite/channel/" + channel)) {
return device;
}
}
return null;
}
private void updateValue(Device device, String label, String value) {
DeviceValue deviceValue = device.getValue(label);
if (deviceValue == null) {
deviceValue = new DeviceValue();
deviceValue.setLabel(label);
deviceValue.setValue(value);
deviceValue.setReadonly(false);
deviceValue.setValueId("{ }");
device.addValue(deviceValue);
} else {
device.setValue(label, value);
}
}
///
/// For intenal commands
///
private class InternalCommands {
private JsonMessaging jsonMessaging;
public InternalCommands() {
Status status = new Status("Noolite-RX-Internal");
if (status.checkExist()) {
status.running();
} else {
status.addIntoDB("Noolite RX Internal", "Service that check incoming Noolite service commands");
}
try {
jsonMessaging = new JsonMessaging(UUID.randomUUID(), "devices-noolite-rx-internal");
jsonMessaging.subscribe("event.devices.noolite.rx.bindchannel");
jsonMessaging.subscribe("event.devices.noolite.rx.unbindchannel");
jsonMessaging.subscribe("event.devices.noolite.rx.unbindallchannel");
jsonMessaging.setNotification(envelope -> {
if (envelope.getObject() instanceof GenericAdvertisement) {
final GenericAdvertisement advertisement = envelope.getObject();
byte channel = Double.valueOf(advertisement.getValue().toString()).byteValue();
switch (advertisement.getLabel()) {
case "BindRXChannel":
LOGGER.debug("Get BindRXChannel advertisement (channel " + channel + ")");
LOGGER.info("Binding device to RX channel " + channel);
rx.bindChannel(channel);
break;
case "UnbindRXChannel":
LOGGER.debug("Get UnbindRXChannel advertisement (channel " + channel + ")");
LOGGER.info("Unbinding device from RX channel " + channel);
rx.unbindChannel(channel);
break;
case "UnbindAllRXChannels":
LOGGER.debug("Get UnbindAllRXChannel advertisement");
LOGGER.info("Unbinding all RX channels");
rx.unbindAllChannels();
break;
}
} else if (envelope.getReceiverInstance() == null) {
// We received unknown broadcast message. Lets make generic log entry.
LOGGER.info("Received broadcast "
+ " from " + envelope.getSenderInstance()
+ " to " + envelope.getReceiverInstance()
+ " at '" + envelope.getSubject()
+ ": " + envelope.getObject());
} else {
// We received unknown request message. Lets make generic log entry.
LOGGER.info("Received request "
+ " from " + envelope.getSenderInstance()
+ " to " + envelope.getReceiverInstance()
+ " at '" + envelope.getSubject()
+ ": " + envelope.getObject());
}
});
jsonMessaging.start();
} catch (final Throwable t) {
LOGGER.error("Error in Noolite-RX-Internal!");
status.crashed();
t.printStackTrace();
}
}
public void stop() {
jsonMessaging.close();
}
}
public void stop() {
messaging.close();
internalCommands.stop();
rx.close();
}
}