Extract Shade struct and create ShadeRepository
This commit is contained in:
18
lib/shade/Shade.h
Normal file
18
lib/shade/Shade.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef SHADE_H
|
||||
#define SHADE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
struct Shade {
|
||||
uint16_t ID;
|
||||
std::string key;
|
||||
std::string friendlyName;
|
||||
int8_t lastTargetPosition;
|
||||
int8_t lastPosition;
|
||||
uint8_t samePositionCount;
|
||||
uint8_t positionFetchCount;
|
||||
void* timer;
|
||||
};
|
||||
|
||||
#endif // SHADE_H
|
||||
66
lib/shade_repository/ShadeRepository.cpp
Normal file
66
lib/shade_repository/ShadeRepository.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "ShadeRepository.h"
|
||||
#include "Shade.h"
|
||||
|
||||
void ShadeRepository::upsert(Shade shade)
|
||||
{
|
||||
for (Shade &existing_shade : shades)
|
||||
{
|
||||
if (existing_shade.key == shade.key)
|
||||
{
|
||||
existing_shade.friendlyName = shade.friendlyName;
|
||||
existing_shade.ID = shade.ID;
|
||||
for (auto &callback : shadeChangedCallbacks) {
|
||||
callback(shade);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find it in the repository, add it
|
||||
shades.push_back(shade);
|
||||
for (auto &callback : shadeAddedCallbacks) {
|
||||
callback(shade);
|
||||
}
|
||||
}
|
||||
|
||||
Shade* ShadeRepository::findById(const uint16_t id)
|
||||
{
|
||||
for (Shade &shade : shades)
|
||||
{
|
||||
if (shade.ID == id)
|
||||
{
|
||||
return &shade;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Shade* ShadeRepository::findByKey(const std::string &key)
|
||||
{
|
||||
for (Shade &shade : shades)
|
||||
{
|
||||
if (shade.key == key)
|
||||
{
|
||||
return &shade;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<Shade>::iterator ShadeRepository::begin()
|
||||
{
|
||||
return shades.begin();
|
||||
}
|
||||
|
||||
std::vector<Shade>::iterator ShadeRepository::end()
|
||||
{
|
||||
return shades.end();
|
||||
}
|
||||
|
||||
void ShadeRepository::addShadeAddedCallback(std::function<void(Shade&)> callback) {
|
||||
shadeAddedCallbacks.push_back(callback);
|
||||
}
|
||||
|
||||
void ShadeRepository::addShadeChangedCallback(std::function<void(Shade&)> callback) {
|
||||
shadeChangedCallbacks.push_back(callback);
|
||||
}
|
||||
25
lib/shade_repository/ShadeRepository.h
Normal file
25
lib/shade_repository/ShadeRepository.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef SHADE_REPOSITORY_H
|
||||
#define SHADE_REPOSITORY_H
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include "Shade.h"
|
||||
|
||||
class ShadeRepository {
|
||||
private:
|
||||
std::vector<Shade> shades;
|
||||
std::vector<std::function<void(Shade&)>> shadeAddedCallbacks;
|
||||
std::vector<std::function<void(Shade&)>> shadeChangedCallbacks;
|
||||
|
||||
public:
|
||||
void upsert(Shade shade);
|
||||
std::vector<Shade>::iterator begin();
|
||||
std::vector<Shade>::iterator end();
|
||||
Shade* findById(const uint16_t id);
|
||||
Shade* findByKey(const std::string& key);
|
||||
|
||||
void addShadeAddedCallback(std::function<void(Shade&)> callback);
|
||||
void addShadeChangedCallback(std::function<void(Shade&)> callback);
|
||||
};
|
||||
|
||||
#endif // SHADE_REPOSITORY_H
|
||||
242
src/main.cpp
242
src/main.cpp
@@ -3,6 +3,8 @@
|
||||
#include <arduino-timer.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <RFPowerView.h>
|
||||
#include "Shade.h"
|
||||
#include "ShadeRepository.h"
|
||||
#include "secrets.h"
|
||||
|
||||
#define SER_BAUDRATE (115200)
|
||||
@@ -26,6 +28,8 @@ EspMQTTClient client(
|
||||
1883
|
||||
);
|
||||
|
||||
ShadeRepository shadeRepository = ShadeRepository();
|
||||
|
||||
#define MAX_FETCH_COUNT (20)
|
||||
auto timer = Timer<10, millis, uint16_t>();
|
||||
|
||||
@@ -49,40 +53,11 @@ void publishBattery(const String& shadeName, const uint8_t battery);
|
||||
|
||||
void publishDiscoveryTopics();
|
||||
|
||||
struct Shade {
|
||||
uint16_t ID;
|
||||
String name;
|
||||
String friendlyName;
|
||||
int8_t lastTargetPosition;
|
||||
int8_t lastPosition;
|
||||
uint8_t samePositionCount;
|
||||
uint8_t positionFetchCount;
|
||||
void* timer;
|
||||
};
|
||||
|
||||
std::vector<Shade> shades;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(SER_BAUDRATE);
|
||||
|
||||
Serial.println("Starting up");
|
||||
|
||||
shades.push_back(Shade{0x4EF1, "study_blind", "Study Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0xA51F, "studio_blockout_left_blind", "Studio Blockout Left Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0xDAEF, "studio_blockout_right_blind", "Studio Blockout Right Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0x7687, "studio_left_blind", "Studio Left Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0xB0DE, "studio_right_blind", "Studio Right Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0xB451, "bedroom_door_blockout_blind", "Bedroom Door Blockout Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0x48A6, "bedroom_window_blockout_blind", "Bedroom Window Blockout Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0x9E14, "bedroom_door_blind", "Bedroom Door Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0x061C, "bedroom_window_blind", "Bedroom Window Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0x2959, "bathroom_blind", "Bathroom Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0x6FAD, "kitchen_door_blind", "Kitchen Door Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0xFB21, "kitchen_window_blind", "Kitchen Window Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0x8B10, "living_room_big_window_blind", "Living Room Big Window Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0x3EB8, "living_room_door_blind", "Living Room Door Blind", -1, -1, 0, 0, nullptr});
|
||||
shades.push_back(Shade{0x5463, "living_room_window_blind", "Living Room Window Blind", -1, -1, 0, 0, nullptr});
|
||||
|
||||
powerView.setPacketReceivedCallback(processPacket);
|
||||
if (!powerView.begin()) {
|
||||
Serial.println("Failed to start RFPowerView");
|
||||
@@ -94,7 +69,37 @@ void setup() {
|
||||
client.enableDebuggingMessages();
|
||||
client.setMaxPacketSize(2048);
|
||||
|
||||
shadeRepository.addShadeAddedCallback([] (Shade& shade) {
|
||||
// Only add shade if client is already connected
|
||||
if (client.isConnected()) {
|
||||
client.subscribe(("hotdog/" + shade.key + "/command").c_str(), processCommandMessage);
|
||||
client.subscribe(("hotdog/" + shade.key + "/set_position").c_str(), processSetPositionMessage);
|
||||
}
|
||||
publishDiscoveryTopics();
|
||||
});
|
||||
|
||||
shadeRepository.addShadeChangedCallback([] (Shade& shade) {
|
||||
publishDiscoveryTopics();
|
||||
});
|
||||
|
||||
delay(100);
|
||||
|
||||
shadeRepository.upsert(Shade{0x4EF1, "study_blind", "Study Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0xA51F, "studio_blockout_left_blind", "Studio Blockout Left Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0xDAEF, "studio_blockout_right_blind", "Studio Blockout Right Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0x7687, "studio_left_blind", "Studio Left Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0xB0DE, "studio_right_blind", "Studio Right Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0xB451, "bedroom_door_blockout_blind", "Bedroom Door Blockout Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0x48A6, "bedroom_window_blockout_blind", "Bedroom Window Blockout Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0x9E14, "bedroom_door_blind", "Bedroom Door Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0x061C, "bedroom_window_blind", "Bedroom Window Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0x2959, "bathroom_blind", "Bathroom Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0x6FAD, "kitchen_door_blind", "Kitchen Door Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0xFB21, "kitchen_window_blind", "Kitchen Window Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0x8B10, "living_room_big_window_blind", "Living Room Big Window Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0x3EB8, "living_room_door_blind", "Living Room Door Blind", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0x5463, "living_room_window_blind", "Living Room Window Blind", -1, -1, 0, 0, nullptr});
|
||||
|
||||
Serial.println("Ready");
|
||||
}
|
||||
|
||||
@@ -135,29 +140,27 @@ void processPacket(const Packet *packet) {
|
||||
for (size_t i = 0; i < parameters.fields.size(); i++) {
|
||||
Field field = parameters.fields[i];
|
||||
if (field.identifier == 0x50) {
|
||||
for (size_t i = 0; i < shades.size(); i++) {
|
||||
if (source == shades[i].ID) {
|
||||
uint16_t value = std::get<uint16_t>(field.value);
|
||||
uint8_t position = (uint8_t)std::round(((float)value / 0xFFFF) * 100);
|
||||
auto shade = shadeRepository.findById(source);
|
||||
if (shade != nullptr) {
|
||||
uint16_t value = std::get<uint16_t>(field.value);
|
||||
uint8_t position = (uint8_t)std::round(((float)value / 0xFFFF) * 100);
|
||||
|
||||
if (shades[i].lastPosition == position) {
|
||||
shades[i].samePositionCount++;
|
||||
} else {
|
||||
shades[i].samePositionCount = 1;
|
||||
}
|
||||
shades[i].lastPosition = position;
|
||||
|
||||
publishPosition(shades[i].name, position);
|
||||
if (shade->lastPosition == position) {
|
||||
shade->samePositionCount++;
|
||||
} else {
|
||||
shade->samePositionCount = 1;
|
||||
}
|
||||
shade->lastPosition = position;
|
||||
|
||||
publishPosition(shade->key.c_str(), position);
|
||||
}
|
||||
} else if (field.identifier == 0x42) {
|
||||
for (size_t i = 0; i < shades.size(); i++) {
|
||||
if (source == shades[i].ID) {
|
||||
uint8_t value = std::get<uint8_t>(field.value);
|
||||
uint8_t battery = uint8_t(((float)value / 200) * 100);
|
||||
|
||||
publishBattery(shades[i].name, battery);
|
||||
}
|
||||
auto shade = shadeRepository.findById(source);
|
||||
if (shade != nullptr) {
|
||||
uint8_t value = std::get<uint8_t>(field.value);
|
||||
uint8_t battery = uint8_t(((float)value / 200) * 100);
|
||||
|
||||
publishBattery(shade->key.c_str(), battery);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,9 +170,9 @@ void processPacket(const Packet *packet) {
|
||||
void onConnectionEstablished() {
|
||||
Serial.println("Connection established");
|
||||
|
||||
for (size_t i = 0; i < shades.size(); i++) {
|
||||
client.subscribe("hotdog/" + shades[i].name + "/command", processCommandMessage);
|
||||
client.subscribe("hotdog/" + shades[i].name + "/set_position", processSetPositionMessage);
|
||||
for (auto shade = shadeRepository.begin(); shade != shadeRepository.end(); shade++) {
|
||||
client.subscribe(("hotdog/" + shade->key + "/command").c_str(), processCommandMessage);
|
||||
client.subscribe(("hotdog/" + shade->key + "/set_position").c_str(), processSetPositionMessage);
|
||||
}
|
||||
|
||||
client.publish("hotdog/availability", "online", true);
|
||||
@@ -276,30 +279,29 @@ bool sendPacket(Packet *packet) {
|
||||
void processCommandMessage(const String &topic, const String &payload) {
|
||||
int startIndex = topic.indexOf("/") + 1;
|
||||
int endIndex = topic.indexOf("/", startIndex);
|
||||
String shadeName = topic.substring(startIndex, endIndex);
|
||||
auto key = topic.substring(startIndex, endIndex).c_str();
|
||||
|
||||
for (size_t i = 0; i < shades.size(); i++) {
|
||||
if (shades[i].name == shadeName) {
|
||||
if (payload == "OPEN") {
|
||||
sendOpenPacket(shades[i].ID);
|
||||
auto shade = shadeRepository.findByKey(key);
|
||||
if (shade != nullptr) {
|
||||
if (payload == "OPEN") {
|
||||
sendOpenPacket(shade->ID);
|
||||
|
||||
startFetchingPosition(shades[i].ID, 100);
|
||||
startFetchingPosition(shade->ID, 100);
|
||||
|
||||
publishState(shades[i].name, "opening");
|
||||
} else if (payload == "CLOSE") {
|
||||
sendClosePacket(shades[i].ID);
|
||||
publishState(shade->key.c_str(), "opening");
|
||||
} else if (payload == "CLOSE") {
|
||||
sendClosePacket(shade->ID);
|
||||
|
||||
startFetchingPosition(shades[i].ID, 0);
|
||||
startFetchingPosition(shade->ID, 0);
|
||||
|
||||
publishState(shades[i].name, "closing");
|
||||
} else if (payload == "STOP") {
|
||||
sendStopPacket(shades[i].ID);
|
||||
publishState(shade->key.c_str(), "closing");
|
||||
} else if (payload == "STOP") {
|
||||
sendStopPacket(shade->ID);
|
||||
|
||||
startFetchingPosition(shades[i].ID, -1);
|
||||
timer.in(100, sendFetchPosition, shades[i].ID);
|
||||
} else if (payload == "REFRESH") {
|
||||
startFetchingPosition(shades[i].ID, -1);
|
||||
}
|
||||
startFetchingPosition(shade->ID, -1);
|
||||
timer.in(100, sendFetchPosition, shade->ID);
|
||||
} else if (payload == "REFRESH") {
|
||||
startFetchingPosition(shade->ID, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,62 +309,59 @@ void processCommandMessage(const String &topic, const String &payload) {
|
||||
void processSetPositionMessage(const String& topic, const String &payload) {
|
||||
int startIndex = topic.indexOf("/") + 1;
|
||||
int endIndex = topic.indexOf("/", startIndex);
|
||||
String shadeName = topic.substring(startIndex, endIndex);
|
||||
auto key = topic.substring(startIndex, endIndex).c_str();
|
||||
|
||||
for (size_t i = 0; i < shades.size(); i++) {
|
||||
if (shades[i].name == shadeName) {
|
||||
float percentage = payload.toInt() / 100.0f;
|
||||
sendSetPosition(shades[i].ID, percentage);
|
||||
auto shade = shadeRepository.findByKey(key);
|
||||
if (shade != nullptr) {
|
||||
float percentage = payload.toInt() / 100.0f;
|
||||
sendSetPosition(shade->ID, percentage);
|
||||
|
||||
if (payload.toInt() > shades[i].lastPosition) {
|
||||
publishState(shades[i].name, "opening");
|
||||
} else if (payload.toInt() < shades[i].lastPosition) {
|
||||
publishState(shades[i].name, "closing");
|
||||
}
|
||||
|
||||
startFetchingPosition(shades[i].ID, payload.toInt());
|
||||
if (payload.toInt() > shade->lastPosition) {
|
||||
publishState(shade->key.c_str(), "opening");
|
||||
} else if (payload.toInt() < shade->lastPosition) {
|
||||
publishState(shade->key.c_str(), "closing");
|
||||
}
|
||||
|
||||
startFetchingPosition(shade->ID, payload.toInt());
|
||||
}
|
||||
}
|
||||
|
||||
bool checkPosition(uint16_t shadeID) {
|
||||
for (size_t i = 0; i < shades.size(); i++) {
|
||||
if (shades[i].ID == shadeID) {
|
||||
// Keep fetching position if:
|
||||
// - the last reported position doesn't match the target position
|
||||
// - a position hasn't been reported yet
|
||||
// - there is no target position
|
||||
if (shades[i].lastTargetPosition != shades[i].lastPosition || shades[i].lastPosition == -1 || shades[i].lastTargetPosition == -1) {
|
||||
// Keep fetching position if the count limits have not been reached
|
||||
if (shades[i].positionFetchCount < MAX_FETCH_COUNT && shades[i].samePositionCount < 2) {
|
||||
shades[i].positionFetchCount++;
|
||||
sendFetchPosition(shadeID);
|
||||
return true;
|
||||
}
|
||||
auto shade = shadeRepository.findById(shadeID);
|
||||
if (shade != nullptr) {
|
||||
// Keep fetching position if:
|
||||
// - the last reported position doesn't match the target position
|
||||
// - a position hasn't been reported yet
|
||||
// - there is no target position
|
||||
if (shade->lastTargetPosition != shade->lastPosition || shade->lastPosition == -1 || shade->lastTargetPosition == -1) {
|
||||
// Keep fetching position if the count limits have not been reached
|
||||
if (shade->positionFetchCount < MAX_FETCH_COUNT && shade->samePositionCount < 2) {
|
||||
shade->positionFetchCount++;
|
||||
sendFetchPosition(shadeID);
|
||||
return true;
|
||||
}
|
||||
|
||||
publishState(shades[i].name, shades[i].lastPosition > 0 ? "open" : "closed");
|
||||
return false;
|
||||
}
|
||||
|
||||
publishState(shade->key.c_str(), shade->lastPosition > 0 ? "open" : "closed");
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void startFetchingPosition(uint16_t shadeID, int8_t targetPosition) {
|
||||
for (size_t i = 0; i < shades.size(); i++) {
|
||||
if (shades[i].ID == shadeID) {
|
||||
// Cancel any existing timer
|
||||
if (shades[i].timer != nullptr) {
|
||||
timer.cancel(shades[i].timer);
|
||||
shades[i].timer = nullptr;
|
||||
}
|
||||
|
||||
shades[i].lastTargetPosition = targetPosition;
|
||||
shades[i].positionFetchCount = 0;
|
||||
shades[i].samePositionCount = 1;
|
||||
|
||||
shades[i].timer = timer.every(2000, checkPosition, shades[i].ID);
|
||||
auto shade = shadeRepository.findById(shadeID);
|
||||
if (shade != nullptr) {
|
||||
// Cancel any existing timer
|
||||
if (shade->timer != nullptr) {
|
||||
timer.cancel(shade->timer);
|
||||
shade->timer = nullptr;
|
||||
}
|
||||
|
||||
shade->lastTargetPosition = targetPosition;
|
||||
shade->positionFetchCount = 0;
|
||||
shade->samePositionCount = 1;
|
||||
|
||||
shade->timer = timer.every(2000, checkPosition, shade->ID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,10 +399,10 @@ void publishCoverDiscoveryTopic(const Shade& shade) {
|
||||
doc["name"] = nullptr;
|
||||
doc["unique_id"] = entityID;
|
||||
doc["availability_topic"] = "hotdog/availability";
|
||||
doc["state_topic"] = "hotdog/" + shade.name + "/state";
|
||||
doc["command_topic"] = "hotdog/" + shade.name + "/command";
|
||||
doc["position_topic"] = "hotdog/" + shade.name + "/position";
|
||||
doc["set_position_topic"] = "hotdog/" + shade.name + "/set_position";
|
||||
doc["state_topic"] = "hotdog/" + shade.key + "/state";
|
||||
doc["command_topic"] = "hotdog/" + shade.key + "/command";
|
||||
doc["position_topic"] = "hotdog/" + shade.key + "/position";
|
||||
doc["set_position_topic"] = "hotdog/" + shade.key + "/set_position";
|
||||
doc["position_open"] = 100;
|
||||
doc["position_closed"] = 0;
|
||||
doc["optimistic"] = false;
|
||||
@@ -425,7 +424,7 @@ void publishBatteryDiscoveryTopic(const Shade& shade) {
|
||||
doc["unique_id"] = entityID;
|
||||
doc["availability_topic"] = "hotdog/availability";
|
||||
doc["device_class"] = "battery";
|
||||
doc["state_topic"] = "hotdog/" + shade.name + "/battery";
|
||||
doc["state_topic"] = "hotdog/" + shade.key + "/battery";
|
||||
|
||||
addDeviceObject(doc, shade);
|
||||
|
||||
@@ -443,7 +442,7 @@ void publishRefreshButtonDiscoveryTopic(const Shade& shade) {
|
||||
doc["name"] = "Refresh";
|
||||
doc["unique_id"] = entityID;
|
||||
doc["availability_topic"] = "hotdog/availability";
|
||||
doc["command_topic"] = "hotdog/" + shade.name + "/command";
|
||||
doc["command_topic"] = "hotdog/" + shade.key + "/command";
|
||||
doc["payload_press"] = "REFRESH";
|
||||
|
||||
addDeviceObject(doc, shade);
|
||||
@@ -454,12 +453,9 @@ void publishRefreshButtonDiscoveryTopic(const Shade& shade) {
|
||||
}
|
||||
|
||||
void publishDiscoveryTopics() {
|
||||
for (size_t i = 0; i < shades.size(); i++) {
|
||||
Shade shade = shades[i];
|
||||
String objectID = String(shade.ID, HEX);
|
||||
|
||||
publishCoverDiscoveryTopic(shade);
|
||||
publishBatteryDiscoveryTopic(shade);
|
||||
publishRefreshButtonDiscoveryTopic(shade);
|
||||
for (auto shade = shadeRepository.begin(); shade != shadeRepository.end(); shade++) {
|
||||
publishCoverDiscoveryTopic(*shade);
|
||||
publishBatteryDiscoveryTopic(*shade);
|
||||
publishRefreshButtonDiscoveryTopic(*shade);
|
||||
}
|
||||
}
|
||||
158
test/test_shade_repository/test_shade_repository.cpp
Normal file
158
test/test_shade_repository/test_shade_repository.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
#include <unity.h>
|
||||
#include <Arduino.h>
|
||||
#include "ShadeRepository.h"
|
||||
|
||||
void setUp()
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown()
|
||||
{
|
||||
}
|
||||
|
||||
void test_shade_is_found_with_id()
|
||||
{
|
||||
ShadeRepository shadeRepository = ShadeRepository();
|
||||
shadeRepository.upsert(Shade{0xABCD, "test_shade", "Test Shade", -1, -1, 0, 0, nullptr});
|
||||
|
||||
auto shade = shadeRepository.findById(0xABCD);
|
||||
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(shade, "shade should not be null");
|
||||
TEST_ASSERT_EQUAL_STRING("Test Shade", shade->friendlyName.c_str());
|
||||
TEST_ASSERT_EQUAL_HEX16(0xABCD, shade->ID);
|
||||
TEST_ASSERT_EQUAL_STRING("test_shade", shade->key.c_str());
|
||||
}
|
||||
|
||||
void test_shade_is_found_with_key()
|
||||
{
|
||||
ShadeRepository shadeRepository = ShadeRepository();
|
||||
shadeRepository.upsert(Shade{0xABCD, "test_shade", "Test Shade", -1, -1, 0, 0, nullptr});
|
||||
|
||||
auto shade = shadeRepository.findByKey("test_shade");
|
||||
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(shade, "shade should not be null");
|
||||
TEST_ASSERT_EQUAL_STRING("Test Shade", shade->friendlyName.c_str());
|
||||
TEST_ASSERT_EQUAL_HEX16(0xABCD, shade->ID);
|
||||
TEST_ASSERT_EQUAL_STRING("test_shade", shade->key.c_str());
|
||||
}
|
||||
|
||||
void test_adding_shade_twice_only_added_once()
|
||||
{
|
||||
ShadeRepository shadeRepository = ShadeRepository();
|
||||
shadeRepository.upsert(Shade{0xABCD, "test_shade", "Test Shade", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0xABCD, "test_shade", "Test Shade", -1, -1, 0, 0, nullptr});
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (auto iter = shadeRepository.begin(); iter != shadeRepository.end(); iter++) {
|
||||
count++;
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, count);
|
||||
}
|
||||
|
||||
void test_updating_shade_id()
|
||||
{
|
||||
ShadeRepository shadeRepository = ShadeRepository();
|
||||
shadeRepository.upsert(Shade{0xABCD, "test_shade", "Test Shade", -1, -1, 0, 0, nullptr});
|
||||
|
||||
auto shade1 = shadeRepository.findById(0xABCD);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX16(0xABCD, shade1->ID);
|
||||
|
||||
shadeRepository.upsert(Shade{0x1234, "test_shade", "Test Shade", -1, -1, 0, 0, nullptr});
|
||||
|
||||
auto shade2 = shadeRepository.findById(0x1234);
|
||||
|
||||
auto shade3 = shadeRepository.findById(0xABCD);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX16(0x1234, shade2->ID);
|
||||
TEST_ASSERT_EQUAL_HEX16(0x1234, shade1->ID);
|
||||
TEST_ASSERT_NULL(shade3);
|
||||
}
|
||||
|
||||
void test_updating_shade_friendly_name()
|
||||
{
|
||||
ShadeRepository shadeRepository = ShadeRepository();
|
||||
shadeRepository.upsert(Shade{0xABCD, "test_shade", "Test Shade", -1, -1, 0, 0, nullptr});
|
||||
|
||||
auto shade1 = shadeRepository.findByKey("test_shade");
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("Test Shade", shade1->friendlyName.c_str());
|
||||
|
||||
shadeRepository.upsert(Shade{0xABCD, "test_shade", "Updated Test Shade", -1, -1, 0, 0, nullptr});
|
||||
|
||||
auto shade2 = shadeRepository.findByKey("test_shade");
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("Updated Test Shade", shade2->friendlyName.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("Updated Test Shade", shade1->friendlyName.c_str());
|
||||
}
|
||||
|
||||
void test_shade_added_callback()
|
||||
{
|
||||
int callbackInvokedCount = 0;
|
||||
|
||||
ShadeRepository shadeRepository = ShadeRepository();
|
||||
shadeRepository.addShadeAddedCallback([&] (Shade& shade) {
|
||||
callbackInvokedCount++;
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("Test Shade", shade.friendlyName.c_str());
|
||||
TEST_ASSERT_EQUAL_HEX16(0xABCD, shade.ID);
|
||||
TEST_ASSERT_EQUAL_STRING("test_shade", shade.key.c_str());
|
||||
});
|
||||
|
||||
shadeRepository.upsert(Shade{0xABCD, "test_shade", "Test Shade", -1, -1, 0, 0, nullptr});
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, callbackInvokedCount);
|
||||
}
|
||||
|
||||
void test_shade_changed_callback()
|
||||
{
|
||||
int callbackInvokedCount = 0;
|
||||
|
||||
ShadeRepository shadeRepository = ShadeRepository();
|
||||
shadeRepository.addShadeChangedCallback([&] (Shade& shade) {
|
||||
callbackInvokedCount++;
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("Updated Test Shade", shade.friendlyName.c_str());
|
||||
TEST_ASSERT_EQUAL_HEX16(0x1234, shade.ID);
|
||||
TEST_ASSERT_EQUAL_STRING("test_shade", shade.key.c_str());
|
||||
});
|
||||
|
||||
shadeRepository.upsert(Shade{0xABCD, "test_shade", "Test Shade", -1, -1, 0, 0, nullptr});
|
||||
shadeRepository.upsert(Shade{0x1234, "test_shade", "Updated Test Shade", -1, -1, 0, 0, nullptr});
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, callbackInvokedCount);
|
||||
}
|
||||
|
||||
int runUnityTests(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_shade_is_found_with_id);
|
||||
RUN_TEST(test_shade_is_found_with_key);
|
||||
RUN_TEST(test_adding_shade_twice_only_added_once);
|
||||
RUN_TEST(test_updating_shade_id);
|
||||
RUN_TEST(test_updating_shade_friendly_name);
|
||||
RUN_TEST(test_shade_added_callback);
|
||||
RUN_TEST(test_shade_changed_callback);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
/**
|
||||
* For Arduino framework
|
||||
*/
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Wait ~2 seconds before the Unity test runner
|
||||
// establishes connection with a board Serial interface
|
||||
delay(2000);
|
||||
|
||||
runUnityTests();
|
||||
}
|
||||
void loop() {}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return runUnityTests();
|
||||
}
|
||||
Reference in New Issue
Block a user