initial push
103
echo-led-controller/echo-led-controller.ino
Normal file
@ -0,0 +1,103 @@
|
||||
//
|
||||
// Echo Serial Led Strip Controller
|
||||
//
|
||||
|
||||
// Serial Connection Variables
|
||||
//
|
||||
const byte serialBufferSize = 100;
|
||||
char serialBuffer[serialBufferSize];
|
||||
const char startMarker = '<';
|
||||
const char endMarker = '>';
|
||||
byte serialBytesReceived = 0;
|
||||
boolean serialReadInProgress = false;
|
||||
boolean newSerialData = false;
|
||||
char serialData[serialBufferSize] = {0};
|
||||
char commandLimiter[2] = ":";
|
||||
|
||||
|
||||
void setup() {
|
||||
// Init Serial Connection
|
||||
Serial.begin(115200);
|
||||
|
||||
// Init LED Strip
|
||||
initLedStrip();
|
||||
|
||||
// Send Init Complete signal
|
||||
Serial.println("<1:1:1>");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Handle Serial Comms
|
||||
handleSerial();
|
||||
|
||||
// Handle Led Strip
|
||||
handleLedStrip();
|
||||
}
|
||||
|
||||
// Serial Handling
|
||||
//
|
||||
void handleSerial() {
|
||||
if(Serial.available() > 0) {
|
||||
char x = Serial.read();
|
||||
|
||||
// the order of these IF clauses is significant
|
||||
if (x == endMarker) {
|
||||
serialReadInProgress = false;
|
||||
newSerialData = true;
|
||||
serialBuffer[serialBytesReceived] = 0;
|
||||
parseData();
|
||||
}
|
||||
|
||||
if(serialReadInProgress) {
|
||||
serialBuffer[serialBytesReceived] = x;
|
||||
serialBytesReceived ++;
|
||||
if (serialBytesReceived == serialBufferSize) {
|
||||
serialBytesReceived = serialBufferSize - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (x == startMarker) {
|
||||
serialBytesReceived = 0;
|
||||
serialReadInProgress = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseData() {
|
||||
char * strtokIndex;
|
||||
char deviceUUID[10];
|
||||
char command[20];
|
||||
char data[69];
|
||||
|
||||
// Get Device UUID
|
||||
strtokIndex = strtok(serialBuffer, ":");
|
||||
strcpy(deviceUUID, strtokIndex);
|
||||
|
||||
strtokIndex = strtok(NULL, ":");
|
||||
strcpy(command, strtokIndex);
|
||||
|
||||
strtokIndex = strtok(NULL, ":");
|
||||
strcpy(data, strtokIndex);
|
||||
|
||||
handleCommand(deviceUUID, command, data);
|
||||
}
|
||||
|
||||
void sendSerialCommand(char* deviceUUID, char* command, char* data) {
|
||||
char serialCommandBuffer[serialBufferSize];
|
||||
char _startMarker[2] = "<";
|
||||
char _endMarker[2] = ">";
|
||||
sprintf(serialCommandBuffer, "%s%s:%s:%s%s", _startMarker, deviceUUID, command, data, _endMarker);
|
||||
Serial.println(serialCommandBuffer);
|
||||
}
|
||||
|
||||
void handleCommand(char* deviceUUID, char* command, char* data) {
|
||||
|
||||
//
|
||||
// Led Strip
|
||||
//
|
||||
|
||||
if (strcmp(deviceUUID, "34gr-54jf") == 0) {
|
||||
ledStripCommand(deviceUUID, command, data);
|
||||
}
|
||||
|
||||
}
|
222
echo-led-controller/echo-led-strip.ino
Normal file
@ -0,0 +1,222 @@
|
||||
#define ledStripPinR 5
|
||||
#define ledStripPinG 6
|
||||
#define ledStripPinB 9
|
||||
|
||||
int currentLedR = 0;
|
||||
int currentLedG = 0;
|
||||
int currentLedB = 0;
|
||||
|
||||
int targetLedR = 0;
|
||||
int targetLedG = 0;
|
||||
int targetLedB = 0;
|
||||
|
||||
int fadeDelay = 5;
|
||||
|
||||
// Modes:
|
||||
// 0 = Off
|
||||
// 1 = Static
|
||||
// 2 = Fade
|
||||
// 3 = Dynamic
|
||||
int currentMode = 2;
|
||||
|
||||
void handleLedStrip () {
|
||||
|
||||
// Check if we need to handle target colors
|
||||
if (currentLedR == targetLedR && currentLedG == targetLedG && currentLedB == targetLedB) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check which mode we are in, and change values based on that
|
||||
if (currentMode == 1) {
|
||||
// Static Mode
|
||||
analogWrite(ledStripPinR, targetLedR);
|
||||
analogWrite(ledStripPinG, targetLedG);
|
||||
analogWrite(ledStripPinB, targetLedB);
|
||||
}
|
||||
|
||||
if (currentMode == 2) {
|
||||
// Fade Mode
|
||||
|
||||
//
|
||||
// Red
|
||||
//
|
||||
|
||||
// Target val is greater, add to current led value
|
||||
if (targetLedR > currentLedR) {
|
||||
currentLedR = currentLedR + 1;
|
||||
}
|
||||
|
||||
// Target val is smaller, remove from current led value
|
||||
if (targetLedR < currentLedR) {
|
||||
currentLedR = currentLedR - 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Green
|
||||
//
|
||||
|
||||
// Target val is greater, add to current led value
|
||||
if (targetLedG > currentLedG) {
|
||||
currentLedG = currentLedG + 1;
|
||||
}
|
||||
|
||||
// Target val is smaller, remove from current led value
|
||||
if (targetLedG < currentLedG) {
|
||||
currentLedG = currentLedG - 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Blue
|
||||
//
|
||||
|
||||
// Target val is greater, add to current led value
|
||||
if (targetLedB > currentLedB) {
|
||||
currentLedB = currentLedB + 1;
|
||||
}
|
||||
|
||||
// Target val is smaller, remove from current led value
|
||||
if (targetLedB < currentLedB) {
|
||||
currentLedB = currentLedB - 1;
|
||||
}
|
||||
|
||||
analogWrite(ledStripPinR, currentLedR);
|
||||
analogWrite(ledStripPinG, currentLedG);
|
||||
analogWrite(ledStripPinB, currentLedB);
|
||||
|
||||
if (currentLedR == targetLedR && currentLedG == targetLedG && currentLedB == targetLedB) {
|
||||
char deviceUUID[10] = "34gr-54jf";
|
||||
char command[20] = "target_reached";
|
||||
char data[69] = "0";
|
||||
sendSerialCommand(deviceUUID, command, data);
|
||||
}
|
||||
|
||||
delay(fadeDelay);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ledStripCommand(char* deviceUUID, char* command, char* data) {
|
||||
if (strcmp(command, "clear_color") == 0) {
|
||||
targetLedR = 0;
|
||||
targetLedG = 0;
|
||||
targetLedB = 0;
|
||||
}
|
||||
|
||||
if (strcmp(command, "set_color") == 0) {
|
||||
setColor(data);
|
||||
}
|
||||
|
||||
if (strcmp(command, "set_mode") == 0) {
|
||||
setMode(data);
|
||||
}
|
||||
|
||||
if (strcmp(command, "set_mode") == 0) {
|
||||
setMode(data);
|
||||
}
|
||||
|
||||
if (strcmp(command, "set_fade_delay") == 0) {
|
||||
setFadeDelay(data);
|
||||
}
|
||||
}
|
||||
|
||||
void setColor(char* dataBuffer) {
|
||||
char * strtokIndex;
|
||||
char rValueRaw[4];
|
||||
char gValueRaw[4];
|
||||
char bValueRaw[4];
|
||||
|
||||
// Get Red Value
|
||||
strtokIndex = strtok(dataBuffer, ",");
|
||||
strcpy(rValueRaw, strtokIndex);
|
||||
|
||||
// Get Green Value
|
||||
strtokIndex = strtok(NULL, ",");
|
||||
strcpy(gValueRaw, strtokIndex);
|
||||
|
||||
// Get Blue Value
|
||||
strtokIndex = strtok(NULL, ",");
|
||||
strcpy(bValueRaw, strtokIndex);
|
||||
|
||||
targetLedR = atoi(rValueRaw);
|
||||
targetLedG = atoi(gValueRaw);
|
||||
targetLedB = atoi(bValueRaw);
|
||||
}
|
||||
|
||||
void setMode(char* dataBuffer) {
|
||||
if (strcmp(dataBuffer, "static") == 0) {
|
||||
currentMode = 1;
|
||||
}
|
||||
|
||||
if (strcmp(dataBuffer, "fade") == 0) {
|
||||
currentMode = 2;
|
||||
}
|
||||
}
|
||||
|
||||
void setFadeDelay(char* dataBuffer) {
|
||||
fadeDelay = atoi(dataBuffer);
|
||||
}
|
||||
|
||||
void initLedStrip() {
|
||||
|
||||
// Define Pin Outputs
|
||||
pinMode(ledStripPinR, OUTPUT);
|
||||
pinMode(ledStripPinG, OUTPUT);
|
||||
pinMode(ledStripPinB, OUTPUT);
|
||||
|
||||
// Write 0 to all rgbs
|
||||
analogWrite(ledStripPinR, 0);
|
||||
analogWrite(ledStripPinG, 0);
|
||||
analogWrite(ledStripPinB, 0);
|
||||
|
||||
// Loop Each Color
|
||||
//
|
||||
|
||||
// Red
|
||||
for (int colorIndex = 0; colorIndex < 256; colorIndex++) {
|
||||
analogWrite(ledStripPinR, colorIndex);
|
||||
delay(3);
|
||||
}
|
||||
|
||||
for (int colorIndex = 255; colorIndex > -1; colorIndex--) {
|
||||
analogWrite(ledStripPinR, colorIndex);
|
||||
delay(3);
|
||||
}
|
||||
|
||||
// Green
|
||||
for (int colorIndex = 0; colorIndex < 256; colorIndex++) {
|
||||
analogWrite(ledStripPinG, colorIndex);
|
||||
delay(3);
|
||||
}
|
||||
|
||||
for (int colorIndex = 255; colorIndex > -1; colorIndex--) {
|
||||
analogWrite(ledStripPinG, colorIndex);
|
||||
delay(3);
|
||||
}
|
||||
|
||||
// Blue
|
||||
for (int colorIndex = 0; colorIndex < 256; colorIndex++) {
|
||||
analogWrite(ledStripPinB, colorIndex);
|
||||
delay(3);
|
||||
}
|
||||
|
||||
for (int colorIndex = 255; colorIndex > -1; colorIndex--) {
|
||||
analogWrite(ledStripPinB, colorIndex);
|
||||
delay(3);
|
||||
}
|
||||
|
||||
// All
|
||||
for (int colorIndex = 0; colorIndex < 256; colorIndex++) {
|
||||
analogWrite(ledStripPinR, colorIndex);
|
||||
analogWrite(ledStripPinG, colorIndex);
|
||||
analogWrite(ledStripPinB, colorIndex);
|
||||
delay(3);
|
||||
}
|
||||
|
||||
for (int colorIndex = 255; colorIndex > -1; colorIndex--) {
|
||||
analogWrite(ledStripPinR, colorIndex);
|
||||
analogWrite(ledStripPinG, colorIndex);
|
||||
analogWrite(ledStripPinB, colorIndex);
|
||||
delay(3);
|
||||
}
|
||||
|
||||
}
|
52
echo-led-controller/echo-main-helpers.ino
Normal file
@ -0,0 +1,52 @@
|
||||
//
|
||||
// Helper functions
|
||||
//
|
||||
|
||||
char* dtostrf(double number, signed char width, unsigned char prec, char *s) {
|
||||
|
||||
if(isnan(number)) {
|
||||
strcpy(s, "nan");
|
||||
return s;
|
||||
}
|
||||
if(isinf(number)) {
|
||||
strcpy(s, "inf");
|
||||
return s;
|
||||
}
|
||||
|
||||
if(number > 4294967040.0 || number < -4294967040.0) {
|
||||
strcpy(s, "ovf");
|
||||
return s;
|
||||
}
|
||||
char* out = s;
|
||||
// Handle negative numbers
|
||||
if(number < 0.0) {
|
||||
*out = '-';
|
||||
++out;
|
||||
number = -number;
|
||||
}
|
||||
|
||||
// Round correctly so that print(1.999, 2) prints as "2.00"
|
||||
double rounding = 0.5;
|
||||
for(uint8_t i = 0; i < prec; ++i)
|
||||
rounding /= 10.0;
|
||||
|
||||
number += rounding;
|
||||
|
||||
// Extract the integer part of the number and print it
|
||||
unsigned long int_part = (unsigned long) number;
|
||||
double remainder = number - (double) int_part;
|
||||
out += sprintf(out, "%d", int_part);
|
||||
|
||||
// Print the decimal point, but only if there are digits beyond
|
||||
if(prec > 0) {
|
||||
*out = '.';
|
||||
++out;
|
||||
}
|
||||
|
||||
while(prec-- > 0) {
|
||||
remainder *= 10.0;
|
||||
}
|
||||
sprintf(out, "%d", (int) remainder);
|
||||
|
||||
return s;
|
||||
}
|
7
echo-main-controller/.vscode/arduino.json
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"board": "arduino:avr:mega",
|
||||
"configuration": "cpu=atmega2560",
|
||||
"port": "/dev/ttyACM0",
|
||||
"sketch": "echo-main-controller.ino",
|
||||
"programmer": "AVR ISP"
|
||||
}
|
28
echo-main-controller/.vscode/c_cpp_properties.json
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"/home/nick/Arduino/libraries/IRremote",
|
||||
"/home/nick/Arduino/libraries/U8g2/src",
|
||||
"/home/nick/Arduino/libraries/DHT_sensor_library",
|
||||
"/home/nick/Arduino/libraries/NewRemoteSwitch",
|
||||
"/home/nick/Software/arduino-1.8.10/tools/**",
|
||||
"/home/nick/Software/arduino-1.8.10/hardware/arduino/avr/**",
|
||||
"/home/nick/Software/arduino-1.8.10/libraries/**",
|
||||
"/home/nick/Software/arduino-1.8.10/hardware/tools/avr/avr/**"
|
||||
],
|
||||
"forcedInclude": [
|
||||
"/home/nick/Software/arduino-1.8.10/hardware/arduino/avr/cores/arduino/Arduino.h"
|
||||
],
|
||||
"intelliSenseMode": "gcc-x64",
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17",
|
||||
"defines": [
|
||||
"USBCON"
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
3
echo-main-controller/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"C_Cpp.errorSquiggles": "Disabled"
|
||||
}
|
35
echo-main-controller/echo-main-ac.ino
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
void airConditionerCommand(char* deviceUUID, char* command, char* data) {
|
||||
|
||||
// Main Air Conditioner
|
||||
if (strcmp(deviceUUID, "70c4-40d2") == 0) {
|
||||
|
||||
if (strcmp(command, "power") == 0) {
|
||||
unsigned int rawData[67] = {8950,4500, 600,550, 600,550, 600,1700, 550,600, 550,600, 550,600, 550,600, 550,600, 550,1700, 600,1650, 600,550, 600,1650, 600,1700, 550,1700, 550,1700, 550,1700, 550,600, 550,600, 600,1650, 600,550, 600,550, 600,600, 550,600, 550,600, 550,1700, 550,1700, 550,600, 550,1700, 600,1650, 600,1650, 600,1700, 550,1700, 550}; // NEC 20DF20DF
|
||||
irTransmitter.sendRaw(rawData, sizeof(rawData) / sizeof(rawData[0]), irTransmitterFreq);
|
||||
Serial.println("whoop");
|
||||
}
|
||||
|
||||
if (strcmp(command, "fanspeed") == 0) {
|
||||
unsigned int rawData[67] = {9000,4500, 550,600, 550,600, 600,1650, 600,550, 600,550, 600,550, 600,600, 550,600, 550,1700, 550,1700, 550,600, 550,1700, 550,1700, 600,1650, 600,1650, 600,1700, 550,600, 550,1700, 550,1700, 550,600, 550,600, 550,600, 600,550, 600,550, 600,1650, 600,600, 550,600, 550,1700, 550,1700, 550,1700, 550,1700, 600,1650, 600}; // NEC 20DF609F
|
||||
irTransmitter.sendRaw(rawData, sizeof(rawData) / sizeof(rawData[0]), irTransmitterFreq);
|
||||
}
|
||||
|
||||
if (strcmp(command, "up") == 0) {
|
||||
unsigned int rawData[67] = {9000,4500, 550,600, 550,600, 550,1700, 550,600, 550,600, 550,600, 600,550, 600,550, 600,1650, 600,1700, 550,600, 550,1700, 550,1700, 550,1700, 550,1700, 600,1650, 600,1650, 600,600, 550,1700, 550,600, 550,600, 550,600, 550,600, 550,600, 550,600, 600,1650, 600,550, 600,1650, 600,1700, 550,1700, 550,1700, 550,1700, 550}; // NEC 20DFA05F
|
||||
irTransmitter.sendRaw(rawData, sizeof(rawData) / sizeof(rawData[0]), irTransmitterFreq);
|
||||
}
|
||||
|
||||
if (strcmp(command, "down") == 0) {
|
||||
unsigned int rawData[67] = {8950,4500, 600,550, 600,550, 600,1650, 600,600, 550,600, 550,600, 550,600, 550,600, 550,1700, 550,1700, 600,550, 600,1650, 600,1650, 600,1700, 550,1700, 550,1700, 550,1700, 550,600, 550,600, 600,550, 600,550, 600,550, 600,550, 600,600, 550,600, 550,1700, 550,1700, 550,1700, 550,1700, 600,1650, 600,1650, 600,1650, 600}; // NEC 20DF807F
|
||||
irTransmitter.sendRaw(rawData, sizeof(rawData) / sizeof(rawData[0]), irTransmitterFreq);
|
||||
}
|
||||
|
||||
if (strcmp(command, "mode") == 0) {
|
||||
unsigned int rawData[67] = {8950,4500, 600,550, 600,550, 600,1700, 550,600, 550,600, 550,600, 550,600, 550,600, 550,1700, 550,1700, 600,550, 600,1650, 600,1650, 600,1700, 550,1700, 550,1700, 550,1700, 550,600, 600,1650, 600,1650, 600,550, 600,600, 550,600, 550,600, 550,600, 550,1700, 550,600, 550,600, 550,1700, 600,1650, 600,1650, 600,1700, 550}; // NEC 20DFB04F
|
||||
irTransmitter.sendRaw(rawData, sizeof(rawData) / sizeof(rawData[0]), irTransmitterFreq);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
17
echo-main-controller/echo-main-blinds.ino
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Sun Blinds
|
||||
//
|
||||
|
||||
void sunBlindsCommand(char* deviceUUID, char* command, char* data) {
|
||||
|
||||
// Main Sun Blinds
|
||||
if (strcmp(deviceUUID, "b039-471a") == 0) {
|
||||
if (strcmp(command, "up") == 0) {
|
||||
sendRFNCommand(0, rf_type_switch, rf_switch_on);
|
||||
}
|
||||
|
||||
if (strcmp(command, "down") == 0) {
|
||||
sendRFNCommand(0, rf_type_switch, rf_switch_off);
|
||||
}
|
||||
}
|
||||
}
|
42
echo-main-controller/echo-main-climate.ino
Normal file
@ -0,0 +1,42 @@
|
||||
// DHT Climate Sensor
|
||||
//
|
||||
#include <DHT.h>
|
||||
#define DHTPIN 6 // Define DHT Connected Data Pin
|
||||
#define DHTTYPE DHT22 // Define DHT Sensor Type
|
||||
DHT dht_main(DHTPIN, DHTTYPE); // Initialize dht Object
|
||||
|
||||
//
|
||||
// Climate Sensors
|
||||
//
|
||||
|
||||
void initClimateSensors() {
|
||||
// Init Main Climate Sensor
|
||||
dht_main.begin();
|
||||
}
|
||||
|
||||
float getClimateTemperature(char* uuid) {
|
||||
if(strcmp(uuid, "aa50-48fd") == 0){
|
||||
return dht_main.readTemperature();
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float getClimateHumidity(char* uuid) {
|
||||
if(strcmp(uuid, "aa50-48fd") == 0){
|
||||
return dht_main.readHumidity();
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void climateSensorCommand(char* deviceUUID, char* command, char* data) {
|
||||
if (strcmp(command, "gather_climate_data") == 0) {
|
||||
char currentTemp[10];
|
||||
dtostrf(getClimateTemperature(deviceUUID), 9, 1, currentTemp);
|
||||
char currentHumidity[10];
|
||||
dtostrf(getClimateHumidity(deviceUUID), 9, 1, currentHumidity);
|
||||
char returnCommand[20] = "climate_data";
|
||||
char dataBuffer[40];
|
||||
sprintf(dataBuffer, "%s,%s", currentTemp, currentHumidity);
|
||||
sendSerialCommand(deviceUUID, returnCommand, dataBuffer);
|
||||
}
|
||||
}
|
240
echo-main-controller/echo-main-controller.ino
Normal file
@ -0,0 +1,240 @@
|
||||
#include <NewRemoteReceiver.h>
|
||||
#include <NewRemoteTransmitter.h>
|
||||
#include <IRremote.h>
|
||||
|
||||
// Serial Connection Variables
|
||||
//
|
||||
const byte serialBufferSize = 100;
|
||||
char serialBuffer[serialBufferSize];
|
||||
const char startMarker = '<';
|
||||
const char endMarker = '>';
|
||||
byte serialBytesReceived = 0;
|
||||
boolean serialReadInProgress = false;
|
||||
boolean newSerialData = false;
|
||||
char serialData[serialBufferSize] = {0};
|
||||
|
||||
char commandLimiter[2] = ":";
|
||||
|
||||
// IR Comms
|
||||
//
|
||||
#define IR_SEND_PIN 9 // NOT CHANGEABLE
|
||||
IRsend irTransmitter;
|
||||
int irTransmitterFreq = 38; // 38KHz NEC Protocol
|
||||
|
||||
// RF Comms
|
||||
//
|
||||
#define RF_SEND_PIN 7
|
||||
char rf_switch_on[10] = "true";
|
||||
char rf_switch_off[10] = "false";
|
||||
char rf_type_switch[10] = "switch";
|
||||
|
||||
// Main Program
|
||||
//
|
||||
|
||||
void setup() {
|
||||
// Init Serial Connection
|
||||
Serial.begin(115200);
|
||||
|
||||
// Init Serial Connection LED Controller
|
||||
Serial1.begin(115200);
|
||||
|
||||
// Init RF Transmitters / Receivers
|
||||
|
||||
// Init Climate Sensors
|
||||
initClimateSensors();
|
||||
|
||||
// Init Movement Sensor
|
||||
initMovementSensor();
|
||||
|
||||
Serial.println("<0:0:0>");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Handle Serial Comms
|
||||
handleSerial();
|
||||
|
||||
// Handle Motion Sensor
|
||||
handleMotionSensor();
|
||||
}
|
||||
|
||||
// Serial Handling
|
||||
//
|
||||
void handleSerial() {
|
||||
if(Serial.available() > 0) {
|
||||
char x = Serial.read();
|
||||
|
||||
// the order of these IF clauses is significant
|
||||
if (x == endMarker) {
|
||||
serialReadInProgress = false;
|
||||
newSerialData = true;
|
||||
serialBuffer[serialBytesReceived] = 0;
|
||||
parseData();
|
||||
}
|
||||
|
||||
if(serialReadInProgress) {
|
||||
serialBuffer[serialBytesReceived] = x;
|
||||
serialBytesReceived ++;
|
||||
if (serialBytesReceived == serialBufferSize) {
|
||||
serialBytesReceived = serialBufferSize - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (x == startMarker) {
|
||||
serialBytesReceived = 0;
|
||||
serialReadInProgress = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(Serial1.available() > 0) {
|
||||
char x = Serial1.read();
|
||||
|
||||
// the order of these IF clauses is significant
|
||||
if (x == endMarker) {
|
||||
serialReadInProgress = false;
|
||||
newSerialData = true;
|
||||
serialBuffer[serialBytesReceived] = 0;
|
||||
parseSerial1Data();
|
||||
}
|
||||
|
||||
if(serialReadInProgress) {
|
||||
serialBuffer[serialBytesReceived] = x;
|
||||
serialBytesReceived ++;
|
||||
if (serialBytesReceived == serialBufferSize) {
|
||||
serialBytesReceived = serialBufferSize - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (x == startMarker) {
|
||||
serialBytesReceived = 0;
|
||||
serialReadInProgress = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseData() {
|
||||
char * strtokIndex;
|
||||
char deviceUUID[10];
|
||||
char command[20];
|
||||
char data[69];
|
||||
|
||||
// Get Device UUID
|
||||
strtokIndex = strtok(serialBuffer, ":");
|
||||
strcpy(deviceUUID, strtokIndex);
|
||||
|
||||
// Get Command
|
||||
strtokIndex = strtok(NULL, ":");
|
||||
strcpy(command, strtokIndex);
|
||||
|
||||
// Get Data
|
||||
strtokIndex = strtok(NULL, ":");
|
||||
strcpy(data, strtokIndex);
|
||||
|
||||
handleCommand(deviceUUID, command, data);
|
||||
}
|
||||
|
||||
void parseSerial1Data() {
|
||||
char * strtokIndex;
|
||||
char deviceUUID[10];
|
||||
char command[20];
|
||||
char data[69];
|
||||
|
||||
// Get Device UUID
|
||||
strtokIndex = strtok(serialBuffer, ":");
|
||||
strcpy(deviceUUID, strtokIndex);
|
||||
|
||||
strtokIndex = strtok(NULL, ":");
|
||||
strcpy(command, strtokIndex);
|
||||
|
||||
strtokIndex = strtok(NULL, ":");
|
||||
strcpy(data, strtokIndex);
|
||||
|
||||
sendSerialCommand(deviceUUID, command, data);
|
||||
}
|
||||
|
||||
void sendSerialCommand(char* deviceUUID, char* command, char* data) {
|
||||
char serialCommandBuffer[serialBufferSize];
|
||||
char _startMarker[2] = "<";
|
||||
char _endMarker[2] = ">";
|
||||
sprintf(serialCommandBuffer, "%s%s:%s:%s%s", _startMarker, deviceUUID, command, data, _endMarker);
|
||||
Serial.println(serialCommandBuffer);
|
||||
}
|
||||
|
||||
|
||||
void sendSerial1Command(char* deviceUUID, char* command, char* data) {
|
||||
char serialCommandBuffer[serialBufferSize];
|
||||
char _startMarker[2] = "<";
|
||||
char _endMarker[2] = ">";
|
||||
sprintf(serialCommandBuffer, "%s%s:%s:%s%s", _startMarker, deviceUUID, command, data, _endMarker);
|
||||
Serial1.println(serialCommandBuffer);
|
||||
}
|
||||
|
||||
|
||||
void handleCommand(char* deviceUUID, char* command, char* data) {
|
||||
//
|
||||
// Climate Sensors
|
||||
//
|
||||
if (strcmp(deviceUUID, "aa50-48fd") == 0) {
|
||||
climateSensorCommand(deviceUUID, command, data);
|
||||
}
|
||||
|
||||
//
|
||||
// Light Sensors
|
||||
//
|
||||
if (strcmp(deviceUUID, "be0c-4bc7") == 0) {
|
||||
lightSensorCommand(deviceUUID, command, data);
|
||||
}
|
||||
|
||||
//
|
||||
// Handle Lights
|
||||
//
|
||||
if (strcmp(deviceUUID, "9472-4f60") == 0) {
|
||||
lightSwitchCommand(deviceUUID, command, data);
|
||||
}
|
||||
|
||||
//
|
||||
// Handle Sun Blinds
|
||||
//
|
||||
|
||||
if (strcmp(deviceUUID, "b039-471a") == 0) {
|
||||
sunBlindsCommand(deviceUUID, command, data);
|
||||
}
|
||||
|
||||
//
|
||||
// Handle Air Conditioner
|
||||
//
|
||||
|
||||
if (strcmp(deviceUUID, "70c4-40d2") == 0) {
|
||||
airConditionerCommand(deviceUUID, command, data);
|
||||
}
|
||||
|
||||
//
|
||||
// Handle LED Strip
|
||||
//
|
||||
if (strcmp(deviceUUID, "34gr-54jf") == 0) {
|
||||
sendSerial1Command(deviceUUID, command, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// RF Trancieving
|
||||
//
|
||||
|
||||
void sendRFNCommand(int unitCode, char* commandType, char* commandValue) {
|
||||
NewRemoteTransmitter mainTransmitter(long(34659090), 7, 255);
|
||||
|
||||
if(strcmp(commandType, "switch") == 0) {
|
||||
|
||||
// True means on
|
||||
if(strcmp(commandValue, "true") == 0) {
|
||||
mainTransmitter.sendUnit(unitCode, true);
|
||||
}
|
||||
|
||||
// False means off
|
||||
if(strcmp(commandValue, "false") == 0) {
|
||||
mainTransmitter.sendUnit(unitCode, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
52
echo-main-controller/echo-main-helpers.ino
Normal file
@ -0,0 +1,52 @@
|
||||
//
|
||||
// Helper functions
|
||||
//
|
||||
|
||||
char* dtostrf(double number, signed char width, unsigned char prec, char *s) {
|
||||
|
||||
if(isnan(number)) {
|
||||
strcpy(s, "nan");
|
||||
return s;
|
||||
}
|
||||
if(isinf(number)) {
|
||||
strcpy(s, "inf");
|
||||
return s;
|
||||
}
|
||||
|
||||
if(number > 4294967040.0 || number < -4294967040.0) {
|
||||
strcpy(s, "ovf");
|
||||
return s;
|
||||
}
|
||||
char* out = s;
|
||||
// Handle negative numbers
|
||||
if(number < 0.0) {
|
||||
*out = '-';
|
||||
++out;
|
||||
number = -number;
|
||||
}
|
||||
|
||||
// Round correctly so that print(1.999, 2) prints as "2.00"
|
||||
double rounding = 0.5;
|
||||
for(uint8_t i = 0; i < prec; ++i)
|
||||
rounding /= 10.0;
|
||||
|
||||
number += rounding;
|
||||
|
||||
// Extract the integer part of the number and print it
|
||||
unsigned long int_part = (unsigned long) number;
|
||||
double remainder = number - (double) int_part;
|
||||
out += sprintf(out, "%d", int_part);
|
||||
|
||||
// Print the decimal point, but only if there are digits beyond
|
||||
if(prec > 0) {
|
||||
*out = '.';
|
||||
++out;
|
||||
}
|
||||
|
||||
while(prec-- > 0) {
|
||||
remainder *= 10.0;
|
||||
}
|
||||
sprintf(out, "%d", (int) remainder);
|
||||
|
||||
return s;
|
||||
}
|
15
echo-main-controller/echo-main-light.ino
Normal file
@ -0,0 +1,15 @@
|
||||
int lightAmount = 0;
|
||||
|
||||
int getLightSensorAmount () {
|
||||
lightAmount = analogRead(A0);
|
||||
return lightAmount;
|
||||
}
|
||||
|
||||
void lightSensorCommand(char* deviceUUID, char* command, char* data) {
|
||||
if (strcmp(command, "gather_light_data") == 0) {
|
||||
char returnCommand[20] = "light_data";
|
||||
char dataBuffer[40];
|
||||
itoa(getLightSensorAmount(), dataBuffer, 10);
|
||||
sendSerialCommand(deviceUUID, returnCommand, dataBuffer);
|
||||
}
|
||||
}
|
19
echo-main-controller/echo-main-lights.ino
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Lights
|
||||
//
|
||||
|
||||
void lightSwitchCommand(char* deviceUUID, char* command, char* data) {
|
||||
|
||||
// Main Light
|
||||
if (strcmp(deviceUUID, "9472-4f60") == 0) {
|
||||
|
||||
if (strcmp(command, "on") == 0) {
|
||||
sendRFNCommand(1, rf_type_switch, rf_switch_on);
|
||||
}
|
||||
|
||||
if (strcmp(command, "off") == 0) {
|
||||
sendRFNCommand(1, rf_type_switch, rf_switch_off);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
34
echo-main-controller/echo-main-movement.ino
Normal file
@ -0,0 +1,34 @@
|
||||
#define MovementSensorPin 5
|
||||
|
||||
int motionDetected = LOW; // Default no motion Detected
|
||||
int motionDetectedAmount = 0; // Amount of motion detected count
|
||||
int motionValue = 0;
|
||||
|
||||
void initMovementSensor() {
|
||||
pinMode(MovementSensorPin, INPUT);
|
||||
}
|
||||
|
||||
void handleMotionSensor() {
|
||||
char deviceUUID[10] = "a328-4229";
|
||||
motionValue = digitalRead(MovementSensorPin);
|
||||
|
||||
// Detected Movement
|
||||
if (motionValue == HIGH) {
|
||||
|
||||
if (motionDetected == LOW) {
|
||||
motionDetected = HIGH;
|
||||
char command[20] = "movement_data";
|
||||
char dataBuffer[40] = "detected_movement";
|
||||
sendSerialCommand(deviceUUID, command, dataBuffer);
|
||||
}
|
||||
} else {
|
||||
|
||||
if(motionDetected == HIGH) {
|
||||
motionDetected = LOW;
|
||||
char command[20] = "movement_data";
|
||||
char dataBuffer[40] = "no_movement";
|
||||
sendSerialCommand(deviceUUID, command, dataBuffer);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
10
echo-master/Dockerfile
Normal file
@ -0,0 +1,10 @@
|
||||
FROM python:3.7
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD [ "python", "./echo.py" ]
|
BIN
echo-master/__pycache__/echo.cpython-37.pyc
Normal file
BIN
echo-master/__pycache__/echo.cpython-38.pyc
Normal file
9
echo-master/docker-compose.yml
Normal file
@ -0,0 +1,9 @@
|
||||
version: '3.4'
|
||||
|
||||
services:
|
||||
echo:
|
||||
build: .
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
ports:
|
||||
- 1337:1337
|
19
echo-master/echo.py
Normal file
@ -0,0 +1,19 @@
|
||||
from echo_api.api import EchoAPI
|
||||
from echo_serial.serial_controller import EchoSerial
|
||||
from echo_scheduler.scheduler import EchoScheduler
|
||||
|
||||
import logging
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Start Echo Serial Handler
|
||||
EchoSerial.init()
|
||||
|
||||
# Start Echo Scheduler
|
||||
EchoScheduler.init()
|
||||
|
||||
# Start Echo API
|
||||
EchoAPI.init()
|
||||
|
||||
|
||||
|
||||
|
1
echo-master/echo_api/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
BIN
echo-master/echo_api/__init__.pyc
Normal file
BIN
echo-master/echo_api/__pycache__/__init__.cpython-37.pyc
Normal file
BIN
echo-master/echo_api/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
echo-master/echo_api/__pycache__/api.cpython-37.pyc
Normal file
BIN
echo-master/echo_api/__pycache__/api.cpython-38.pyc
Normal file
BIN
echo-master/echo_api/__pycache__/auth.cpython-37.pyc
Normal file
BIN
echo-master/echo_api/__pycache__/auth.cpython-38.pyc
Normal file
BIN
echo-master/echo_api/__pycache__/device.cpython-37.pyc
Normal file
BIN
echo-master/echo_api/__pycache__/device.cpython-38.pyc
Normal file
BIN
echo-master/echo_api/__pycache__/devices.cpython-37.pyc
Normal file
BIN
echo-master/echo_api/__pycache__/devices.cpython-38.pyc
Normal file
BIN
echo-master/echo_api/__pycache__/devices_endpoint.cpython-37.pyc
Normal file
43
echo-master/echo_api/api.py
Normal file
@ -0,0 +1,43 @@
|
||||
import flask
|
||||
|
||||
from flask import request, abort, Flask, g
|
||||
from flask_restful import Resource, Api
|
||||
from flask_cors import CORS, cross_origin
|
||||
|
||||
import echo_api.auth as Auth
|
||||
|
||||
# Endpoints
|
||||
import echo_api.devices as DevicesAPI
|
||||
import echo_api.device as DeviceInfoAPI
|
||||
import echo_api.device as DeviceControlAPI
|
||||
|
||||
class EchoAPI:
|
||||
|
||||
# Initialize Flask API Object
|
||||
app = flask.Flask(__name__)
|
||||
cors = CORS(app)
|
||||
app.config['CORS_HEADERS'] = 'Content-Type'
|
||||
api = Api(app)
|
||||
|
||||
# Start API
|
||||
@staticmethod
|
||||
def init():
|
||||
EchoAPI.app.run(host='0.0.0.0', port=1337, debug=False)
|
||||
|
||||
@staticmethod
|
||||
@app.route('/')
|
||||
def index():
|
||||
if not Auth.Auth.verify(request):
|
||||
return {'status': 'failed', 'message': 'Not Authenticated!'}, 401
|
||||
|
||||
return {'status': 'success', 'message': 'Echo API is healthy and running!'}, 200
|
||||
|
||||
@staticmethod
|
||||
@app.errorhandler(404)
|
||||
def not_found(error):
|
||||
return {'status': 'failed', 'message': 'Endpoint not found!'}, 404
|
||||
|
||||
api.add_resource(DevicesAPI.DevicesAPI, '/devices')
|
||||
api.add_resource(DeviceInfoAPI.DeviceInfoAPI, '/device/<string:uuid>')
|
||||
api.add_resource(DeviceControlAPI.DeviceControlAPI, '/device')
|
||||
|
BIN
echo-master/echo_api/api.pyc
Normal file
14
echo-master/echo_api/auth.py
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
class Auth:
|
||||
|
||||
tokens = {
|
||||
'Bearer f8c85565-ece7-471e-999f-9bb6a7b61a4c': 'Nick Leeman Development'
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def verify(req):
|
||||
token = req.headers.get('Authorization')
|
||||
if token in Auth.tokens:
|
||||
return True
|
||||
else:
|
||||
return False
|
56
echo-master/echo_api/device.py
Normal file
@ -0,0 +1,56 @@
|
||||
from flask_restful import Resource, Api, reqparse
|
||||
from flask import request
|
||||
from bson import json_util, ObjectId
|
||||
import json
|
||||
|
||||
import echo_db.database as EchoDB
|
||||
import echo_api.auth as Auth
|
||||
import echo_controller.controller as Controller
|
||||
|
||||
class DeviceInfoAPI(Resource):
|
||||
|
||||
def get(self, uuid):
|
||||
# Verify Auth
|
||||
if not Auth.Auth.verify(request):
|
||||
return {'status': 'failed', 'message': 'Not Authenticated!'}, 401
|
||||
|
||||
# Get Device
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
device = collection.find_one({"uuid": uuid})
|
||||
|
||||
# Check if Device Exists
|
||||
if device == None:
|
||||
return {'status': 'failed', 'message': 'Device not found!', 'data': {}}, 404
|
||||
|
||||
# Format and respond
|
||||
device_json = json.loads(json_util.dumps(device))
|
||||
return {'status': 'success', 'data': device_json}, 200
|
||||
|
||||
class DeviceControlAPI(Resource):
|
||||
def post(self):
|
||||
if not Auth.Auth.verify(request):
|
||||
return {'status': 'failed', 'message': 'Not Authenticated!'}, 401
|
||||
|
||||
parser = reqparse.RequestParser()
|
||||
parser.add_argument('uuid', required = True)
|
||||
parser.add_argument('action', required = True)
|
||||
parser.add_argument('data', required = False, default = {})
|
||||
|
||||
# Parse the arguments into an object
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get Device
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
device = collection.find_one({'uuid': args['uuid']})
|
||||
|
||||
# Check if Device Exists
|
||||
if device == None:
|
||||
return {'status': 'failed', 'message': 'Device not found!'}, 200
|
||||
|
||||
if args['action'] not in device['actions']:
|
||||
return {'status': 'failed', 'message': 'Action not found!'}, 200
|
||||
|
||||
# Send Action to Echo Controller
|
||||
return Controller.Controller.handle_action(device, args['action'], args['data'])
|
||||
|
||||
|
18
echo-master/echo_api/devices.py
Normal file
@ -0,0 +1,18 @@
|
||||
from flask_restful import Resource, Api, reqparse
|
||||
from flask import request
|
||||
from bson import json_util, ObjectId
|
||||
import json
|
||||
|
||||
import echo_db.database as EchoDB
|
||||
import echo_api.auth as Auth
|
||||
|
||||
class DevicesAPI(Resource):
|
||||
|
||||
def get(self):
|
||||
if not Auth.Auth.verify(request):
|
||||
return {'status': 'failed', 'message': 'Not Authenticated!'}, 401
|
||||
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
devices = collection.find({})
|
||||
devices_json = json.loads(json_util.dumps(devices))
|
||||
return {'status': 'success', 'data': devices_json}, 200
|
0
echo-master/echo_controller/__init__.py
Normal file
BIN
echo-master/echo_controller/__pycache__/__init__.cpython-37.pyc
Normal file
BIN
echo-master/echo_controller/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
echo-master/echo_controller/__pycache__/blinds.cpython-37.pyc
Normal file
BIN
echo-master/echo_controller/__pycache__/blinds.cpython-38.pyc
Normal file
BIN
echo-master/echo_controller/__pycache__/led_strip.cpython-38.pyc
Normal file
BIN
echo-master/echo_controller/__pycache__/sensor.cpython-37.pyc
Normal file
BIN
echo-master/echo_controller/__pycache__/sensor.cpython-38.pyc
Normal file
BIN
echo-master/echo_controller/__pycache__/switch.cpython-37.pyc
Normal file
BIN
echo-master/echo_controller/__pycache__/switch.cpython-38.pyc
Normal file
177
echo-master/echo_controller/air_conditioner.py
Normal file
@ -0,0 +1,177 @@
|
||||
import datetime
|
||||
import time
|
||||
import sys
|
||||
import echo_serial.serial_controller as EchoSerial
|
||||
import echo_db.database as EchoDB
|
||||
import _thread
|
||||
|
||||
class AirConditioner:
|
||||
|
||||
@staticmethod
|
||||
def handle_action(device, action, data):
|
||||
actionData = device['actions'][action]
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
|
||||
# Check if a thread is using the blinds, if so return.
|
||||
if device['status'] == "busy":
|
||||
return {'status': 'failed', 'message': 'Air Conditioner is being controlled by Echo, Please wait till finished.'}, 200
|
||||
|
||||
if action == "on":
|
||||
|
||||
if device['status'] == "on":
|
||||
return {'status': 'failed', 'message': 'Air conditioning is already on!'}, 200
|
||||
|
||||
# Update Database
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'on' }})
|
||||
|
||||
EchoSerial.EchoSerial.send_commmand(device, "power")
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
if action == "off":
|
||||
|
||||
if device['status'] == "off":
|
||||
return {'status': 'failed', 'message': 'Air conditioning is already off!'}, 200
|
||||
|
||||
EchoSerial.EchoSerial.send_commmand(device, "power")
|
||||
|
||||
# Update Database
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'off' }})
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
if action == "set_fan_speed":
|
||||
selected_speed = data
|
||||
|
||||
if selected_speed == {}:
|
||||
return {'status': 'failed', 'message': 'Please select a speed!'}, 200
|
||||
|
||||
if selected_speed == device['fan_speed']:
|
||||
return {'status': 'failed', 'message': 'Fan speed already set to requested speed!'}, 200
|
||||
|
||||
if device['status'] == "off":
|
||||
return {'status': 'failed', 'message': 'Air Conditioner must be on to change fan speeds!'}, 200
|
||||
|
||||
EchoSerial.EchoSerial.send_commmand(device, "fanspeed")
|
||||
|
||||
if device['fan_speed'] == "low":
|
||||
# We just put the speed to high
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'fan_speed': 'high' }})
|
||||
|
||||
if device['fan_speed'] == "high":
|
||||
# We just put the speed to high
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'fan_speed': 'low' }})
|
||||
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
if action == "set_mode":
|
||||
selected_mode = data
|
||||
|
||||
if selected_mode == {}:
|
||||
return {'status': 'failed', 'message': 'Please select a mode!'}, 200
|
||||
|
||||
if device['status'] == "off":
|
||||
return {'status': 'failed', 'message': 'Air Conditioniner must be on to change modes!'}, 200
|
||||
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'busy' }})
|
||||
|
||||
# Start thread because this action has waiting functions
|
||||
_thread.start_new_thread(AirConditioner.handleSetMode, ("Main Handle Set Mode Thread", 2, device, selected_mode))
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
if action == "set_temperature":
|
||||
selected_temperature = data
|
||||
|
||||
if selected_temperature == {}:
|
||||
return {'status': 'failed', 'message': 'Please select a temperature!'}, 200
|
||||
|
||||
if device['status'] == "off":
|
||||
return {'status': 'failed', 'message': 'Air Conditioning must be on to change target temperatures!'}, 200
|
||||
|
||||
new_selected_temperature = int(selected_temperature)
|
||||
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'busy' }})
|
||||
|
||||
# Start thread because this action has waiting functions
|
||||
_thread.start_new_thread(AirConditioner.handleSetTemperature, ("Main Handle Set Temperature Thread", 2, device, new_selected_temperature))
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
@staticmethod
|
||||
def handleSetTemperature(threadName, delay, device, temperature):
|
||||
print('Started Air Conditioner Set Termperature Thread!')
|
||||
|
||||
if device['target_temperature'] == temperature:
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'on' }})
|
||||
print('Exiting Air Conditioner Set Termperature Thread!')
|
||||
sys.exit()
|
||||
|
||||
if temperature > device['target_temperature']:
|
||||
temperature_direction = "up"
|
||||
new_temperature_degrees = temperature - device['target_temperature']
|
||||
|
||||
if temperature < device['target_temperature']:
|
||||
temperature_direction = "down"
|
||||
new_temperature_degrees = device['target_temperature'] - temperature
|
||||
|
||||
# Plus one to init temp change on ac
|
||||
for x in range(new_temperature_degrees + 1):
|
||||
EchoSerial.EchoSerial.send_commmand(device, temperature_direction)
|
||||
time.sleep(.3)
|
||||
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'on', 'target_temperature': temperature }})
|
||||
print('Exiting Air Conditioner Set Termperature Thread!')
|
||||
|
||||
@staticmethod
|
||||
def handleSetMode(threadName, delay, device, mode):
|
||||
print('Started Air Conditioner Set Mode Thread!')
|
||||
|
||||
if device['mode'] == mode:
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'on' }})
|
||||
print('Exiting Air Conditioner Set Mode Thread!')
|
||||
sys.exit()
|
||||
|
||||
# Goto base Pos (cooling)
|
||||
if device['mode'] == "cooling":
|
||||
#Do nothing, already in cooling mode (base pos)
|
||||
pass
|
||||
|
||||
if device['mode'] == "dehumidifying":
|
||||
# switch 2 times
|
||||
EchoSerial.EchoSerial.send_commmand(device, "mode")
|
||||
time.sleep(.3)
|
||||
EchoSerial.EchoSerial.send_commmand(device, "mode")
|
||||
time.sleep(.3)
|
||||
|
||||
if device['mode'] == "blowing":
|
||||
# switch 1 time
|
||||
EchoSerial.EchoSerial.send_commmand(device, "mode")
|
||||
time.sleep(.3)
|
||||
|
||||
# Do nothing, base pos is cooling
|
||||
if mode == "cooling":
|
||||
pass
|
||||
|
||||
if mode == "dehumidifying":
|
||||
# switch 1 time
|
||||
EchoSerial.EchoSerial.send_commmand(device, "mode")
|
||||
time.sleep(.3)
|
||||
|
||||
if mode == "blowing":
|
||||
# switch 2 times
|
||||
EchoSerial.EchoSerial.send_commmand(device, "mode")
|
||||
time.sleep(.3)
|
||||
EchoSerial.EchoSerial.send_commmand(device, "mode")
|
||||
time.sleep(.3)
|
||||
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'on', 'mode': mode }})
|
||||
print('Exiting Air Conditioner Set Mode Thread!')
|
161
echo-master/echo_controller/blinds.py
Normal file
@ -0,0 +1,161 @@
|
||||
import datetime
|
||||
import time
|
||||
import sys
|
||||
import echo_serial.serial_controller as EchoSerial
|
||||
import echo_db.database as EchoDB
|
||||
import _thread
|
||||
|
||||
class Blinds:
|
||||
|
||||
@staticmethod
|
||||
def handle_action(device, action, data):
|
||||
actionData = device['actions'][action]
|
||||
|
||||
# Check if we send action too fast after last issued action
|
||||
actionNow = datetime.datetime.now()
|
||||
actionLast = device['last_used']
|
||||
lastIssued = (actionNow - actionLast).total_seconds()
|
||||
|
||||
if lastIssued < 2.0:
|
||||
return {'status': 'failed', 'message': 'Issued action too fast after last issued action!'}, 200
|
||||
|
||||
# Check if a thread is using the blinds, if so return.
|
||||
if device['status'] == "busy":
|
||||
return {'status': 'failed', 'message': 'Blinds are being controlled by Echo, Please wait till finished.'}, 200
|
||||
|
||||
# Check if we issued stop command and check if we are moving
|
||||
# Stop blinds if we are moving
|
||||
if action == "stop":
|
||||
if device['status'] == "go_down" or device['status'] == "go_up":
|
||||
return Blinds.stopActiveBlinds(device, action, data)
|
||||
else:
|
||||
return {'status': 'failed', 'message': 'Blinds are not moving!'}, 200
|
||||
|
||||
# Check if we are already going up or down
|
||||
# If we are, stop that action
|
||||
if device['status'] == "go_down" or device['status'] == "go_up":
|
||||
return Blinds.stopActiveBlinds(device, action, data)
|
||||
|
||||
# If we are requesting specific preset
|
||||
if action == "preset":
|
||||
selected_preset = data
|
||||
|
||||
if selected_preset == {}:
|
||||
return {'status': 'failed', 'message': 'Please select a preset!'}, 200
|
||||
|
||||
if selected_preset not in device['presets']:
|
||||
return {'status': 'failed', 'message': 'Preset does not exist!'}, 200
|
||||
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'busy' }})
|
||||
|
||||
# Start thread because this action has waiting functions
|
||||
_thread.start_new_thread(Blinds.handleBlindsPreset, ("Main Serial Thread", 2, device, selected_preset))
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
# If we are requesting action
|
||||
if action == "up" or action == "down":
|
||||
# Send Serial Command
|
||||
EchoSerial.EchoSerial.send_commmand(device, action)
|
||||
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'go_' + action }})
|
||||
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
# If we are requesting to set automatic time
|
||||
if action == "set_automatic_time":
|
||||
selected_auto_time = data
|
||||
|
||||
if selected_auto_time == {}:
|
||||
return {'status': 'failed', 'message': 'Please select a time!'}, 200
|
||||
|
||||
if selected_auto_time == "--:--":
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'automatic_open': False, 'automatic_open_time': '--:--' }})
|
||||
else:
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'automatic_open': True, 'automatic_open_time': selected_auto_time }})
|
||||
|
||||
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def stopActiveBlinds(device, action, data):
|
||||
if device['status'] == "go_down":
|
||||
EchoSerial.EchoSerial.send_commmand(device, "down")
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'custom' }})
|
||||
return {'status': 'success', 'message': 'Stopped blinds.'}, 200
|
||||
|
||||
if device['status'] == "go_up":
|
||||
EchoSerial.EchoSerial.send_commmand(device, "up")
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'custom' }})
|
||||
return {'status': 'success', 'message': 'Stopped blinds.'}, 200
|
||||
|
||||
|
||||
@staticmethod
|
||||
def handleBlindsPreset(threadname, delay, device, preset):
|
||||
print('Started Blinds Preset Thread!')
|
||||
presetData = device['presets'][preset]
|
||||
|
||||
# Check if we are already in preset position
|
||||
if device['status'] == "preset":
|
||||
if device['current_preset'] == preset:
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'custom' }})
|
||||
print('Exiting Blinds Preset Thread!')
|
||||
sys.exit()
|
||||
|
||||
# If custom goto open position
|
||||
if device['status'] == "custom":
|
||||
Blinds.setBlindsOpen(device)
|
||||
|
||||
# If device is already in preset position, check if we need to go to open position first
|
||||
# Or if we can skip that
|
||||
if device['status'] == "preset":
|
||||
if device['current_preset'] != "open":
|
||||
Blinds.setBlindsOpen(device)
|
||||
|
||||
if preset == "closed_ventilated":
|
||||
# Go down for 11 seconds
|
||||
EchoSerial.EchoSerial.send_commmand(device, 'down')
|
||||
time.sleep(17)
|
||||
EchoSerial.EchoSerial.send_commmand(device, 'down')
|
||||
|
||||
if preset == "open_ventilated":
|
||||
# Go down for 11 seconds
|
||||
EchoSerial.EchoSerial.send_commmand(device, 'down')
|
||||
time.sleep(11)
|
||||
EchoSerial.EchoSerial.send_commmand(device, 'down')
|
||||
|
||||
if preset == "open":
|
||||
# We already went to open position, no need to do it again
|
||||
pass
|
||||
|
||||
if preset == "closed":
|
||||
EchoSerial.EchoSerial.send_commmand(device, 'down')
|
||||
time.sleep(19)
|
||||
EchoSerial.EchoSerial.send_commmand(device, 'down')
|
||||
|
||||
# Update Database
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': 'preset', 'current_preset': preset }})
|
||||
print('Exiting Blinds Preset Thread!')
|
||||
|
||||
@staticmethod
|
||||
def setBlindsOpen(device):
|
||||
EchoSerial.EchoSerial.send_commmand(device, 'up')
|
||||
time.sleep(25)
|
||||
EchoSerial.EchoSerial.send_commmand(device, 'up')
|
||||
time.sleep(2)
|
31
echo-master/echo_controller/controller.py
Normal file
@ -0,0 +1,31 @@
|
||||
import echo_controller.switch as Switch
|
||||
import echo_controller.sensor as Sensor
|
||||
import echo_controller.blinds as Blinds
|
||||
import echo_controller.air_conditioner as AirConditioner
|
||||
import echo_controller.led_strip as LedStrip
|
||||
|
||||
class Controller:
|
||||
|
||||
# These are all the device types with actions
|
||||
device_action_controls = {
|
||||
'switch' : Switch.Switch.handle_action,
|
||||
'sensor' : Sensor.Sensor.handle_action,
|
||||
'blinds' : Blinds.Blinds.handle_action,
|
||||
'airconditioner' : AirConditioner.AirConditioner.handle_action,
|
||||
'led_strip': LedStrip.LEDStrip.handle_action
|
||||
}
|
||||
|
||||
# These are all the output devices
|
||||
device_output_controls = {
|
||||
'sensor' : Sensor.Sensor.handle_data
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def handle_action(device, action, data = {}):
|
||||
device_type = device['type']
|
||||
return Controller.device_action_controls[device_type](device, action, data)
|
||||
|
||||
@staticmethod
|
||||
def handle_output_device(device, action, data):
|
||||
device_type = device['type']
|
||||
return Controller.device_output_controls[device_type](device, action, data)
|
75
echo-master/echo_controller/led_strip.py
Normal file
@ -0,0 +1,75 @@
|
||||
import datetime
|
||||
import time
|
||||
import sys
|
||||
import echo_serial.serial_controller as EchoSerial
|
||||
import echo_db.database as EchoDB
|
||||
import _thread
|
||||
|
||||
class LEDStrip:
|
||||
|
||||
@staticmethod
|
||||
def handle_action(device, action, data):
|
||||
actionData = device['actions'][action]
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
|
||||
# Check if we send action too fast after last issued action
|
||||
actionNow = datetime.datetime.now()
|
||||
actionLast = device['last_used']
|
||||
lastIssued = (actionNow - actionLast).total_seconds()
|
||||
|
||||
# if lastIssued < 2.0:
|
||||
# return {'status': 'failed', 'message': 'Issued action too fast after last issued action!'}, 200
|
||||
|
||||
# Check if a thread is using the blinds, if so return.
|
||||
if device['status'] == "busy":
|
||||
return {'status': 'failed', 'message': 'LED Strip is being controlled by Echo, Please wait till finished.'}, 200
|
||||
|
||||
# Stop blinds if we are moving
|
||||
if action == "off":
|
||||
EchoSerial.EchoSerial.send_commmand(device, 'clear_color')
|
||||
|
||||
# Update Database
|
||||
collection.update_one({'uuid': device['uuid']}, {
|
||||
"$set": { 'last_used': datetime.datetime.now(),
|
||||
"status": action
|
||||
}})
|
||||
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
if action == "on":
|
||||
EchoSerial.EchoSerial.send_commmand(device, 'set_color', device['current_color'])
|
||||
|
||||
# Update Database
|
||||
collection.update_one({'uuid': device['uuid']}, {
|
||||
"$set": { 'last_used': datetime.datetime.now(),
|
||||
"status": action
|
||||
}})
|
||||
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
if action == "set_color":
|
||||
EchoSerial.EchoSerial.send_commmand(device, 'clear_color', data)
|
||||
|
||||
# Update Database
|
||||
collection.update_one({'uuid': device['uuid']}, {
|
||||
"$set": { 'last_used': datetime.datetime.now(),
|
||||
"current_color": data
|
||||
}})
|
||||
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
if action == "automatic_movement":
|
||||
|
||||
if data == "on":
|
||||
# Update Database
|
||||
now_time = datetime.datetime.now()
|
||||
auto_shutdown = now_time + datetime.timedelta(minutes = 2)
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'automatic_movement': True, 'automatic_shutdown': auto_shutdown }})
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
if data == "off":
|
||||
# Update Database
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'automatic_movement': False }})
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
return {'status': 'failed', 'message': 'Setting not found!'}, 200
|
105
echo-master/echo_controller/sensor.py
Normal file
@ -0,0 +1,105 @@
|
||||
import datetime
|
||||
import echo_serial.serial_controller as EchoSerial
|
||||
import echo_db.database as EchoDB
|
||||
from bson import json_util, ObjectId
|
||||
import json
|
||||
|
||||
import echo_controller.switch as Switch
|
||||
|
||||
class Sensor:
|
||||
|
||||
@staticmethod
|
||||
def handle_action(device, action, data):
|
||||
actionData = device['actions'][action]
|
||||
|
||||
#
|
||||
# Climate Sensors
|
||||
#
|
||||
|
||||
if action == "gather_climate_data":
|
||||
EchoSerial.EchoSerial.send_commmand(device, action)
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
if action == "get_climate_data":
|
||||
collection = EchoDB.EchoDB.database['ClimateLogs']
|
||||
data = collection.find().sort([('timestamp', -1)]).limit(1)
|
||||
data_json = json.loads(json_util.dumps(data))
|
||||
return {'status': 'success', 'message': actionData['message'], 'data': data_json}, 200
|
||||
|
||||
#
|
||||
# Light Sensors
|
||||
#
|
||||
|
||||
if action == "gather_light_data":
|
||||
EchoSerial.EchoSerial.send_commmand(device, action)
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
if action == "get_light_data":
|
||||
collection = EchoDB.EchoDB.database['LightLog']
|
||||
data = collection.find().sort([('timestamp', -1)]).limit(1)
|
||||
data_json = json.loads(json_util.dumps(data))
|
||||
return {'status': 'success', 'message': actionData['message'], 'data': data_json}, 200
|
||||
|
||||
#
|
||||
# Movement Sensors
|
||||
#
|
||||
|
||||
if action == "get_movment_data":
|
||||
collection = EchoDB.EchoDB.database['MovementLog']
|
||||
data = collection.find().sort([('timestamp', -1)]).limit(20)
|
||||
data_json = json.loads(json_util.dumps(data))
|
||||
return {'status': 'success', 'message': actionData['message'], 'data': data_json}, 200
|
||||
|
||||
@staticmethod
|
||||
def handle_data(device, action, data):
|
||||
#
|
||||
# Climate Sensors
|
||||
#
|
||||
|
||||
if action == 'climate_data':
|
||||
collection = EchoDB.EchoDB.database['ClimateLogs']
|
||||
climate_data = data[2].split(',')
|
||||
|
||||
logData = {
|
||||
'uuid': device['uuid'],
|
||||
'temperature': float(climate_data[0]),
|
||||
'humidity': float(climate_data[1]),
|
||||
'timestamp': datetime.datetime.now()
|
||||
}
|
||||
|
||||
collection.insert(logData)
|
||||
|
||||
#
|
||||
# Light Sensors
|
||||
#
|
||||
|
||||
if action == 'light_data':
|
||||
collection = EchoDB.EchoDB.database['LightLog']
|
||||
lightAmount = data[2]
|
||||
|
||||
logData = {
|
||||
'uuid': device['uuid'],
|
||||
'light_amount': int(lightAmount),
|
||||
'timestamp': datetime.datetime.now()
|
||||
}
|
||||
|
||||
collection.insert(logData)
|
||||
|
||||
#
|
||||
# Movement Sensors
|
||||
#
|
||||
|
||||
if action == 'movement_data':
|
||||
collection = EchoDB.EchoDB.database['MovementLog']
|
||||
typeMovement = data[2]
|
||||
|
||||
logData = {
|
||||
'uuid': device['uuid'],
|
||||
'type_movement': typeMovement,
|
||||
'timestamp': datetime.datetime.now()
|
||||
}
|
||||
|
||||
collection.insert(logData)
|
||||
|
||||
# Handle Automatic Lights
|
||||
Switch.Switch.handle_movement_switch()
|
79
echo-master/echo_controller/switch.py
Normal file
@ -0,0 +1,79 @@
|
||||
import datetime
|
||||
import echo_serial.serial_controller as EchoSerial
|
||||
import echo_db.database as EchoDB
|
||||
|
||||
class Switch:
|
||||
|
||||
@staticmethod
|
||||
def handle_action(device, action, data):
|
||||
actionData = device['actions'][action]
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
|
||||
if action == "automatic_movement":
|
||||
|
||||
if data == "on":
|
||||
# Update Database
|
||||
now_time = datetime.datetime.now()
|
||||
auto_shutdown = now_time + datetime.timedelta(minutes= 2)
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'automatic_movement': True, 'automatic_shutdown': auto_shutdown }})
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
if data == "off":
|
||||
# Update Database
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'automatic_movement': False }})
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
return {'status': 'failed', 'message': 'Setting not found!'}, 200
|
||||
|
||||
#Check if status is already the action
|
||||
if device['status'] == action:
|
||||
message = "Device is already in the requested state!"
|
||||
return {'status': 'failed', 'message': message}, 200
|
||||
|
||||
# Send Serial Command
|
||||
EchoSerial.EchoSerial.send_commmand(device, action)
|
||||
|
||||
# Update Database
|
||||
collection.update_one({'uuid': device['uuid']}, {"$set": { 'last_used': datetime.datetime.now(), 'status': action }})
|
||||
|
||||
return {'status': 'success', 'message': actionData['message']}, 200
|
||||
|
||||
|
||||
@staticmethod
|
||||
def handle_movement_switch():
|
||||
mainLightUUID = '9472-4f60'
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
|
||||
# Get Device (Main Light)
|
||||
mainLightDevice = collection.find_one({'uuid': mainLightUUID})
|
||||
|
||||
# Check if automatic movement mode of main light is enabled, else do nothing
|
||||
if mainLightDevice['automatic_movement'] == False:
|
||||
return
|
||||
|
||||
# Check if light levels are low enough to turn on light
|
||||
collectionLight = EchoDB.EchoDB.database['LightLog']
|
||||
data = collectionLight.find().sort([('timestamp', -1)]).limit(1)
|
||||
if data[0]['light_amount'] > 250:
|
||||
return;
|
||||
|
||||
# Light is already on, update automatic shutdown.
|
||||
if mainLightDevice['status'] == "on":
|
||||
now_time = datetime.datetime.now()
|
||||
auto_shutdown = now_time + datetime.timedelta(minutes= 2)
|
||||
collection.update_one({'uuid': mainLightUUID}, {"$set": { 'automatic_shutdown': auto_shutdown }})
|
||||
return
|
||||
|
||||
# Light is off, turn it on
|
||||
|
||||
# Send Serial Command
|
||||
EchoSerial.EchoSerial.send_commmand(mainLightDevice, "on")
|
||||
|
||||
# Auto shutdown time
|
||||
now_time = datetime.datetime.now()
|
||||
auto_shutdown = now_time + datetime.timedelta(minutes= 2)
|
||||
|
||||
# Update Database
|
||||
collection.update_one({'uuid': mainLightUUID}, {"$set": { 'last_used': datetime.datetime.now(), 'status': "on", 'automatic_shutdown': auto_shutdown }})
|
||||
|
||||
|
0
echo-master/echo_db/__init__.py
Normal file
BIN
echo-master/echo_db/__pycache__/__init__.cpython-37.pyc
Normal file
BIN
echo-master/echo_db/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
echo-master/echo_db/__pycache__/database.cpython-37.pyc
Normal file
BIN
echo-master/echo_db/__pycache__/database.cpython-38.pyc
Normal file
BIN
echo-master/echo_db/__pycache__/devices.cpython-37.pyc
Normal file
22
echo-master/echo_db/database.py
Normal file
@ -0,0 +1,22 @@
|
||||
import pymongo
|
||||
import sys
|
||||
|
||||
class EchoDB:
|
||||
|
||||
# Connect to MongoDB
|
||||
try:
|
||||
print('Connecting to MongoDB Database...')
|
||||
mongo_client = pymongo.MongoClient('mongodb://admin:ASnick1AS@172.16.100.244:27017/?authSource=admin')
|
||||
print('Connected to MongoDB Database!')
|
||||
except:
|
||||
print('Error occured while connecting to MongoDB Database!')
|
||||
print(sys.exc_info()[0])
|
||||
quit()
|
||||
|
||||
# Select Database
|
||||
database = mongo_client['Echo']
|
||||
|
||||
# Create Indexes
|
||||
database['MovementLog'].create_index("timestamp", expireAfterSeconds=10)
|
||||
database['ClimateLog'].create_index("timestamp", expireAfterSeconds=10)
|
||||
database['LightLog'].create_index("timestamp", expireAfterSeconds=10)
|
0
echo-master/echo_scheduler/__init__.py
Normal file
BIN
echo-master/echo_scheduler/__pycache__/__init__.cpython-37.pyc
Normal file
BIN
echo-master/echo_scheduler/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
echo-master/echo_scheduler/__pycache__/scheduler.cpython-37.pyc
Normal file
BIN
echo-master/echo_scheduler/__pycache__/scheduler.cpython-38.pyc
Normal file
100
echo-master/echo_scheduler/scheduler.py
Normal file
@ -0,0 +1,100 @@
|
||||
import sys
|
||||
import _thread
|
||||
import datetime
|
||||
import time
|
||||
import schedule
|
||||
|
||||
import echo_controller.controller as Controller
|
||||
import echo_db.database as EchoDB
|
||||
|
||||
class EchoScheduler:
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def init():
|
||||
print('Starting Echo Scheduler Thread...')
|
||||
_thread.start_new_thread(EchoScheduler.scheduler, ("Main Scheduler Thread", 2, ))
|
||||
|
||||
@staticmethod
|
||||
def scheduler(threadname, delay):
|
||||
print('Echo Scheduler Thread started!')
|
||||
|
||||
# Schedule Tasks
|
||||
schedule.every(2).minutes.do(EchoScheduler.getClimateData)
|
||||
schedule.every(2).minutes.do(EchoScheduler.getLightData)
|
||||
schedule.every(1).minute.do(EchoScheduler.checkAutomaticShutdown)
|
||||
schedule.every(1).minute.do(EchoScheduler.checkAutomaticBlinds)
|
||||
|
||||
# collection_delete = EchoDB.EchoDB.database['ClimateLogs']
|
||||
# collection_delete.remove({})
|
||||
|
||||
while True:
|
||||
schedule.run_pending()
|
||||
time.sleep(1)
|
||||
|
||||
@staticmethod
|
||||
def getClimateData():
|
||||
# Get Device
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
device = collection.find_one({'uuid': 'aa50-48fd'})
|
||||
|
||||
# Call Action
|
||||
Controller.Controller.handle_action(device, "gather_climate_data")
|
||||
|
||||
print('Scheduler Gathered Climate Data')
|
||||
|
||||
@staticmethod
|
||||
def getLightData():
|
||||
# Get Device
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
device = collection.find_one({'uuid': 'be0c-4bc7'})
|
||||
|
||||
# Call Action
|
||||
Controller.Controller.handle_action(device, "gather_light_data")
|
||||
|
||||
print('Scheduler Gathered Light Data')
|
||||
|
||||
@staticmethod
|
||||
def checkAutomaticShutdown():
|
||||
# Get Device
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
device = collection.find_one({'uuid': '9472-4f60'})
|
||||
|
||||
# Check if light is on
|
||||
if device['status'] == "off":
|
||||
return;
|
||||
|
||||
# Check if automatic movement is enabled
|
||||
if device['automatic_movement'] == False:
|
||||
return;
|
||||
|
||||
# Check if date is current time is past device automatic shutdown time
|
||||
automatic_shutdown_time = device['automatic_shutdown']
|
||||
current_time = datetime.datetime.now()
|
||||
|
||||
if current_time > automatic_shutdown_time:
|
||||
Controller.Controller.handle_action(device, "off")
|
||||
print('Scheduler Turned off light')
|
||||
|
||||
@staticmethod
|
||||
def checkAutomaticBlinds():
|
||||
# Get current time
|
||||
now_time = datetime.datetime.now()
|
||||
now_hours = now_time.hour
|
||||
now_minutes = now_time.minute
|
||||
check_time = str(now_hours).zfill(2) + ":" + str(now_minutes).zfill(2)
|
||||
|
||||
# Get Device
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
device = collection.find_one({'uuid': 'b039-471a'})
|
||||
|
||||
# Check if automatic open is enabled
|
||||
if device['automatic_open'] == False:
|
||||
return;
|
||||
|
||||
automatic_open_time = device['automatic_open_time']
|
||||
|
||||
if automatic_open_time == check_time:
|
||||
Controller.Controller.handle_action(device, "preset", "open")
|
||||
print('Scheduler Opened Sunblinds')
|
0
echo-master/echo_serial/__init__.py
Normal file
BIN
echo-master/echo_serial/__pycache__/__init__.cpython-37.pyc
Normal file
BIN
echo-master/echo_serial/__pycache__/__init__.cpython-38.pyc
Normal file
122
echo-master/echo_serial/serial_controller.py
Normal file
@ -0,0 +1,122 @@
|
||||
import serial
|
||||
import _thread
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
|
||||
import echo_db.database as EchoDB
|
||||
import echo_controller.controller as Controller
|
||||
|
||||
class EchoSerial:
|
||||
|
||||
arduino = serial.Serial()
|
||||
allow_control = True
|
||||
|
||||
@staticmethod
|
||||
def init():
|
||||
try:
|
||||
print('Starting Serial Connection with arduino...')
|
||||
# Start Serial Connection
|
||||
EchoSerial.arduino.baudrate = 115200
|
||||
EchoSerial.arduino.port = '/dev/ttyACM0'
|
||||
EchoSerial.arduino.timeout = .1
|
||||
EchoSerial.arduino.open()
|
||||
|
||||
if not EchoSerial.arduino.is_open:
|
||||
print('Could not open Serial Connection with arduino!')
|
||||
|
||||
print('Serial Connection Opened with arduino!')
|
||||
print('Starting Serial Handling Thread...')
|
||||
_thread.start_new_thread(EchoSerial.handle_serial, ("Main Serial Thread", 2, ))
|
||||
except:
|
||||
print('Error Initializing Serial Controller!')
|
||||
print('Serial Error: ' + str(sys.exc_info()[0]))
|
||||
EchoSerial.allow_control = False
|
||||
|
||||
@staticmethod
|
||||
def handle_serial(threadname, delay):
|
||||
print('Serial Handling Thread Started!')
|
||||
while EchoSerial.arduino.isOpen():
|
||||
time.sleep(.02)
|
||||
try:
|
||||
data = EchoSerial.arduino.readline()[:-2]
|
||||
if data:
|
||||
# Parse Incoming Data > Decode to UTF-8
|
||||
parsed_data = data.decode("utf-8")
|
||||
|
||||
# Start new thread to handle the reponse
|
||||
_thread.start_new_thread(EchoSerial.handle_serial_data, ("Serial Response Handler Tread", 2, parsed_data))
|
||||
|
||||
except:
|
||||
print('Error Occured on Serial handling Thread!')
|
||||
print('Serial Error: ' + str(sys.exc_info()[0]))
|
||||
EchoSerial.allow_control = False
|
||||
EchoSerial.reopen_serial_connection()
|
||||
# _thread.interrupt_main()
|
||||
|
||||
@staticmethod
|
||||
def reopen_serial_connection():
|
||||
try:
|
||||
EchoSerial.arduino.close()
|
||||
except:
|
||||
print("Error closing serial connection")
|
||||
|
||||
while not EchoSerial.arduino.isOpen():
|
||||
try:
|
||||
EchoSerial.arduino.open()
|
||||
except:
|
||||
print("Error reopening serial connection")
|
||||
finally:
|
||||
time.sleep(1)
|
||||
|
||||
EchoSerial.allow_control = True
|
||||
print("Serial Connection Established!")
|
||||
|
||||
@staticmethod
|
||||
def handle_serial_data(threadNamee, delay, data):
|
||||
# Command Example:
|
||||
# 9472-4f60:temp_data:data
|
||||
# Data is if needed separated by ,
|
||||
print('Parsing Packet: ' + data)
|
||||
|
||||
# Remove packet Markers
|
||||
data = data[1:-1]
|
||||
|
||||
# Parse command
|
||||
parsed_data = data.split(':')
|
||||
|
||||
# Check if startup command
|
||||
if parsed_data[0] == "0":
|
||||
print('Echo Controller Ready!')
|
||||
return
|
||||
|
||||
# Check if startup command
|
||||
if parsed_data[0] == "1":
|
||||
print('Echo LED Controller Ready!')
|
||||
return
|
||||
|
||||
# Get Device
|
||||
collection = EchoDB.EchoDB.database['Devices']
|
||||
device = collection.find_one({'uuid': parsed_data[0]})
|
||||
|
||||
# Send to controller
|
||||
Controller.Controller.handle_output_device(device, parsed_data[1], parsed_data)
|
||||
|
||||
@staticmethod
|
||||
def send_serial(command):
|
||||
# Command Example:
|
||||
# 9472-4f60:get_data:data
|
||||
# data is if needed separated by ,
|
||||
EchoSerial.arduino.write(command.encode())
|
||||
|
||||
@staticmethod
|
||||
def send_commmand(device, action, value = "0"):
|
||||
serial_command = "<" + device['uuid'] + ":" + action + ":" + value + ">"
|
||||
print('Sending Packet: ' + serial_command)
|
||||
EchoSerial.send_serial(serial_command)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
6
echo-master/requirements.txt
Normal file
@ -0,0 +1,6 @@
|
||||
Flask
|
||||
Flask-RESTful
|
||||
pymongo
|
||||
pyserial
|
||||
schedule
|
||||
flask-cors
|
31
echo-mobile-app/echo/.gitignore
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
# Specifies intentionally untracked files to ignore when using Git
|
||||
# http://git-scm.com/docs/gitignore
|
||||
|
||||
*~
|
||||
*.sw[mnpcod]
|
||||
.tmp
|
||||
*.tmp
|
||||
*.tmp.*
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
UserInterfaceState.xcuserstate
|
||||
$RECYCLE.BIN/
|
||||
|
||||
*.log
|
||||
log.txt
|
||||
npm-debug.log*
|
||||
|
||||
/.idea
|
||||
/.ionic
|
||||
/.sass-cache
|
||||
/.sourcemaps
|
||||
/.versions
|
||||
/.vscode
|
||||
/coverage
|
||||
/dist
|
||||
/node_modules
|
||||
/platforms
|
||||
/plugins
|
||||
/www
|
187
echo-mobile-app/echo/angular.json
Normal file
@ -0,0 +1,187 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"defaultProject": "app",
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"app": {
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"schematics": {},
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "www",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "src/assets",
|
||||
"output": "assets"
|
||||
},
|
||||
{
|
||||
"glob": "**/*.svg",
|
||||
"input": "node_modules/ionicons/dist/ionicons/svg",
|
||||
"output": "./svg"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
{
|
||||
"input": "src/theme/variables.scss"
|
||||
},
|
||||
{
|
||||
"input": "src/global.scss"
|
||||
}
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ci": {
|
||||
"progress": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "app:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "app:build:production"
|
||||
},
|
||||
"ci": {
|
||||
"progress": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "app:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"styles": [],
|
||||
"scripts": [],
|
||||
"assets": [
|
||||
{
|
||||
"glob": "favicon.ico",
|
||||
"input": "src/",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "src/assets",
|
||||
"output": "/assets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"progress": false,
|
||||
"watch": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "app:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "app:serve:production"
|
||||
},
|
||||
"ci": {
|
||||
"devServerTarget": "app:serve:ci"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ionic-cordova-build": {
|
||||
"builder": "@ionic/angular-toolkit:cordova-build",
|
||||
"options": {
|
||||
"browserTarget": "app:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "app:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ionic-cordova-serve": {
|
||||
"builder": "@ionic/angular-toolkit:cordova-serve",
|
||||
"options": {
|
||||
"cordovaBuildTarget": "app:ionic-cordova-build",
|
||||
"devServerTarget": "app:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"cordovaBuildTarget": "app:ionic-cordova-build:production",
|
||||
"devServerTarget": "app:serve:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"defaultCollection": "@ionic/angular-toolkit"
|
||||
},
|
||||
"schematics": {
|
||||
"@ionic/angular-toolkit:component": {
|
||||
"styleext": "scss"
|
||||
},
|
||||
"@ionic/angular-toolkit:page": {
|
||||
"styleext": "scss"
|
||||
}
|
||||
}
|
||||
}
|
12
echo-mobile-app/echo/browserslist
Normal file
@ -0,0 +1,12 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
110
echo-mobile-app/echo/config.xml
Normal file
@ -0,0 +1,110 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="io.ionic.starter" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<name>Echo</name>
|
||||
<description>Home Control made by Nick Leeman</description>
|
||||
<author email="hi@ionicframework.com" href="http://ionicframework.com/">Nick Leeman</author>
|
||||
<content src="index.html" />
|
||||
<access origin="*" />
|
||||
<preferance name="android-usesCleartextTraffic" value="true" />
|
||||
<allow-navigation href="*" />
|
||||
<allow-intent href="http://*/*" />
|
||||
<allow-intent href="https://*/*" />
|
||||
<allow-intent href="tel:*" />
|
||||
<allow-intent href="sms:*" />
|
||||
<allow-intent href="mailto:*" />
|
||||
<allow-intent href="geo:*" />
|
||||
<preference name="ScrollEnabled" value="false" />
|
||||
<preference name="android-minSdkVersion" value="19" />
|
||||
<preference name="BackupWebStorage" value="none" />
|
||||
<preference name="SplashMaintainAspectRatio" value="true" />
|
||||
<preference name="FadeSplashScreenDuration" value="300" />
|
||||
<preference name="SplashShowOnlyFirstTime" value="false" />
|
||||
<preference name="SplashScreen" value="screen" />
|
||||
<preference name="SplashScreenDelay" value="3000" />
|
||||
<platform name="android">
|
||||
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application android:usesCleartextTraffic="true" />
|
||||
<application android:networkSecurityConfig="@xml/network_security_config" />
|
||||
</edit-config>
|
||||
<resource-file src="resources/android/xml/network_security_config.xml" target="app/src/main/res/xml/network_security_config.xml" />
|
||||
<allow-intent href="market:*" />
|
||||
<icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
|
||||
<icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" />
|
||||
<icon density="hdpi" src="resources/android/icon/drawable-hdpi-icon.png" />
|
||||
<icon density="xhdpi" src="resources/android/icon/drawable-xhdpi-icon.png" />
|
||||
<icon density="xxhdpi" src="resources/android/icon/drawable-xxhdpi-icon.png" />
|
||||
<icon density="xxxhdpi" src="resources/android/icon/drawable-xxxhdpi-icon.png" />
|
||||
<splash density="land-ldpi" src="resources/android/splash/drawable-land-ldpi-screen.png" />
|
||||
<splash density="land-mdpi" src="resources/android/splash/drawable-land-mdpi-screen.png" />
|
||||
<splash density="land-hdpi" src="resources/android/splash/drawable-land-hdpi-screen.png" />
|
||||
<splash density="land-xhdpi" src="resources/android/splash/drawable-land-xhdpi-screen.png" />
|
||||
<splash density="land-xxhdpi" src="resources/android/splash/drawable-land-xxhdpi-screen.png" />
|
||||
<splash density="land-xxxhdpi" src="resources/android/splash/drawable-land-xxxhdpi-screen.png" />
|
||||
<splash density="port-ldpi" src="resources/android/splash/drawable-port-ldpi-screen.png" />
|
||||
<splash density="port-mdpi" src="resources/android/splash/drawable-port-mdpi-screen.png" />
|
||||
<splash density="port-hdpi" src="resources/android/splash/drawable-port-hdpi-screen.png" />
|
||||
<splash density="port-xhdpi" src="resources/android/splash/drawable-port-xhdpi-screen.png" />
|
||||
<splash density="port-xxhdpi" src="resources/android/splash/drawable-port-xxhdpi-screen.png" />
|
||||
<splash density="port-xxxhdpi" src="resources/android/splash/drawable-port-xxxhdpi-screen.png" />
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
<icon height="57" src="resources/ios/icon/icon.png" width="57" />
|
||||
<icon height="114" src="resources/ios/icon/icon@2x.png" width="114" />
|
||||
<icon height="29" src="resources/ios/icon/icon-small.png" width="29" />
|
||||
<icon height="58" src="resources/ios/icon/icon-small@2x.png" width="58" />
|
||||
<icon height="87" src="resources/ios/icon/icon-small@3x.png" width="87" />
|
||||
<icon height="20" src="resources/ios/icon/icon-20.png" width="20" />
|
||||
<icon height="40" src="resources/ios/icon/icon-20@2x.png" width="40" />
|
||||
<icon height="60" src="resources/ios/icon/icon-20@3x.png" width="60" />
|
||||
<icon height="48" src="resources/ios/icon/icon-24@2x.png" width="48" />
|
||||
<icon height="55" src="resources/ios/icon/icon-27.5@2x.png" width="55" />
|
||||
<icon height="29" src="resources/ios/icon/icon-29.png" width="29" />
|
||||
<icon height="58" src="resources/ios/icon/icon-29@2x.png" width="58" />
|
||||
<icon height="87" src="resources/ios/icon/icon-29@3x.png" width="87" />
|
||||
<icon height="40" src="resources/ios/icon/icon-40.png" width="40" />
|
||||
<icon height="80" src="resources/ios/icon/icon-40@2x.png" width="80" />
|
||||
<icon height="120" src="resources/ios/icon/icon-40@3x.png" width="120" />
|
||||
<icon height="88" src="resources/ios/icon/icon-44@2x.png" width="88" />
|
||||
<icon height="50" src="resources/ios/icon/icon-50.png" width="50" />
|
||||
<icon height="100" src="resources/ios/icon/icon-50@2x.png" width="100" />
|
||||
<icon height="60" src="resources/ios/icon/icon-60.png" width="60" />
|
||||
<icon height="120" src="resources/ios/icon/icon-60@2x.png" width="120" />
|
||||
<icon height="180" src="resources/ios/icon/icon-60@3x.png" width="180" />
|
||||
<icon height="72" src="resources/ios/icon/icon-72.png" width="72" />
|
||||
<icon height="144" src="resources/ios/icon/icon-72@2x.png" width="144" />
|
||||
<icon height="76" src="resources/ios/icon/icon-76.png" width="76" />
|
||||
<icon height="152" src="resources/ios/icon/icon-76@2x.png" width="152" />
|
||||
<icon height="167" src="resources/ios/icon/icon-83.5@2x.png" width="167" />
|
||||
<icon height="172" src="resources/ios/icon/icon-86@2x.png" width="172" />
|
||||
<icon height="196" src="resources/ios/icon/icon-98@2x.png" width="196" />
|
||||
<icon height="1024" src="resources/ios/icon/icon-1024.png" width="1024" />
|
||||
<splash height="480" src="resources/ios/splash/Default~iphone.png" width="320" />
|
||||
<splash height="960" src="resources/ios/splash/Default@2x~iphone.png" width="640" />
|
||||
<splash height="1024" src="resources/ios/splash/Default-Portrait~ipad.png" width="768" />
|
||||
<splash height="768" src="resources/ios/splash/Default-Landscape~ipad.png" width="1024" />
|
||||
<splash height="1125" src="resources/ios/splash/Default-Landscape-2436h.png" width="2436" />
|
||||
<splash height="1242" src="resources/ios/splash/Default-Landscape-736h.png" width="2208" />
|
||||
<splash height="2048" src="resources/ios/splash/Default-Portrait@2x~ipad.png" width="1536" />
|
||||
<splash height="1536" src="resources/ios/splash/Default-Landscape@2x~ipad.png" width="2048" />
|
||||
<splash height="2732" src="resources/ios/splash/Default-Portrait@~ipadpro.png" width="2048" />
|
||||
<splash height="2048" src="resources/ios/splash/Default-Landscape@~ipadpro.png" width="2732" />
|
||||
<splash height="1136" src="resources/ios/splash/Default-568h@2x~iphone.png" width="640" />
|
||||
<splash height="1334" src="resources/ios/splash/Default-667h.png" width="750" />
|
||||
<splash height="2208" src="resources/ios/splash/Default-736h.png" width="1242" />
|
||||
<splash height="2436" src="resources/ios/splash/Default-2436h.png" width="1125" />
|
||||
<splash height="2732" src="resources/ios/splash/Default@2x~universal~anyany.png" width="2732" />
|
||||
<icon height="216" src="resources/ios/icon/icon-108@2x.png" width="216" />
|
||||
<splash height="2688" src="resources/ios/splash/Default-2688h~iphone.png" width="1242" />
|
||||
<splash height="1242" src="resources/ios/splash/Default-Landscape-2688h~iphone.png" width="2688" />
|
||||
<splash height="1792" src="resources/ios/splash/Default-1792h~iphone.png" width="828" />
|
||||
<splash height="828" src="resources/ios/splash/Default-Landscape-1792h~iphone.png" width="1792" />
|
||||
</platform>
|
||||
<plugin name="cordova-plugin-whitelist" spec="1.3.3" />
|
||||
<plugin name="cordova-plugin-statusbar" spec="2.4.2" />
|
||||
<plugin name="cordova-plugin-device" spec="2.0.2" />
|
||||
<plugin name="cordova-plugin-splashscreen" spec="5.0.2" />
|
||||
<plugin name="cordova-plugin-ionic-webview" spec="^4.0.0" />
|
||||
<plugin name="cordova-plugin-ionic-keyboard" spec="^2.0.5" />
|
||||
</widget>
|
28
echo-mobile-app/echo/e2e/protractor.conf.js
Normal file
@ -0,0 +1,28 @@
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {}
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
14
echo-mobile-app/echo/e2e/src/app.e2e-spec.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { AppPage } from './app.po';
|
||||
|
||||
describe('new App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
it('should be blank', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getParagraphText()).toContain('The world is your oyster.');
|
||||
});
|
||||
});
|
11
echo-mobile-app/echo/e2e/src/app.po.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo() {
|
||||
return browser.get('/');
|
||||
}
|
||||
|
||||
getParagraphText() {
|
||||
return element(by.deepCss('app-root ion-content')).getText();
|
||||
}
|
||||
}
|
13
echo-mobile-app/echo/e2e/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
7
echo-mobile-app/echo/ionic.config.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "echo",
|
||||
"integrations": {
|
||||
"cordova": {}
|
||||
},
|
||||
"type": "angular"
|
||||
}
|
31
echo-mobile-app/echo/karma.conf.js
Normal file
@ -0,0 +1,31 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../coverage'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false
|
||||
});
|
||||
};
|
12580
echo-mobile-app/echo/package-lock.json
generated
Normal file
83
echo-mobile-app/echo/package.json
Normal file
@ -0,0 +1,83 @@
|
||||
{
|
||||
"name": "echo",
|
||||
"version": "0.0.1",
|
||||
"author": "Ionic Framework",
|
||||
"homepage": "https://ionicframework.com/",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build --prod",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "~8.1.2",
|
||||
"@angular/core": "~8.1.2",
|
||||
"@angular/forms": "~8.1.2",
|
||||
"@angular/platform-browser": "~8.1.2",
|
||||
"@angular/platform-browser-dynamic": "~8.1.2",
|
||||
"@angular/router": "~8.1.2",
|
||||
"@ionic-native/core": "^5.0.0",
|
||||
"@ionic-native/splash-screen": "^5.0.0",
|
||||
"@ionic-native/status-bar": "^5.0.0",
|
||||
"@ionic/angular": "^4.7.1",
|
||||
"chart.js": "^2.9.3",
|
||||
"cordova-android": "8.1.0",
|
||||
"core-js": "^2.5.4",
|
||||
"ng2-charts": "^2.3.0",
|
||||
"ngx-color": "^5.1.4",
|
||||
"rxjs": "~6.5.1",
|
||||
"tslib": "^1.9.0",
|
||||
"zone.js": "~0.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/architect": "~0.801.2",
|
||||
"@angular-devkit/build-angular": "~0.801.2",
|
||||
"@angular-devkit/core": "~8.1.2",
|
||||
"@angular-devkit/schematics": "~8.1.2",
|
||||
"@angular/cli": "~8.1.2",
|
||||
"@angular/compiler": "~8.1.2",
|
||||
"@angular/compiler-cli": "~8.1.2",
|
||||
"@angular/language-service": "~8.1.2",
|
||||
"@ionic/angular-toolkit": "^2.1.1",
|
||||
"@types/jasmine": "~3.3.8",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "~8.9.4",
|
||||
"codelyzer": "^5.0.0",
|
||||
"cordova-plugin-device": "^2.0.2",
|
||||
"cordova-plugin-ionic-keyboard": "^2.2.0",
|
||||
"cordova-plugin-ionic-webview": "^4.1.3",
|
||||
"cordova-plugin-splashscreen": "^5.0.2",
|
||||
"cordova-plugin-statusbar": "^2.4.2",
|
||||
"cordova-plugin-whitelist": "^1.3.3",
|
||||
"jasmine-core": "~3.4.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~4.1.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||
"karma-jasmine": "~2.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.0",
|
||||
"protractor": "~5.4.0",
|
||||
"ts-node": "~7.0.0",
|
||||
"tslint": "~5.15.0",
|
||||
"typescript": "~3.4.3"
|
||||
},
|
||||
"description": "An Ionic project",
|
||||
"cordova": {
|
||||
"plugins": {
|
||||
"cordova-plugin-whitelist": {},
|
||||
"cordova-plugin-statusbar": {},
|
||||
"cordova-plugin-device": {},
|
||||
"cordova-plugin-splashscreen": {},
|
||||
"cordova-plugin-ionic-webview": {
|
||||
"ANDROID_SUPPORT_ANNOTATIONS_VERSION": "27.+"
|
||||
},
|
||||
"cordova-plugin-ionic-keyboard": {}
|
||||
},
|
||||
"platforms": [
|
||||
"android"
|
||||
]
|
||||
}
|
||||
}
|
8
echo-mobile-app/echo/resources/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
These are Cordova resources. You can replace icon.png and splash.png and run
|
||||
`ionic cordova resources` to generate custom icons and splash screens for your
|
||||
app. See `ionic cordova resources --help` for details.
|
||||
|
||||
Cordova reference documentation:
|
||||
|
||||
- Icons: https://cordova.apache.org/docs/en/latest/config_ref/images.html
|
||||
- Splash Screens: https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-splashscreen/
|
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 11 KiB |