initial commit
This commit is contained in:
56
include/addr.hpp
Normal file
56
include/addr.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
struct DaliCompareAddrResult {
|
||||
int retH = 0;
|
||||
int retM = 0;
|
||||
int retL = 0;
|
||||
int nextAddr = 0;
|
||||
};
|
||||
|
||||
class DaliAddr {
|
||||
public:
|
||||
explicit DaliAddr(DaliBase& base);
|
||||
|
||||
std::vector<int> onlineDevices;
|
||||
bool isSearching = false;
|
||||
int scanRangeStart = 0;
|
||||
int scanRangeEnd = 63;
|
||||
|
||||
bool isAllocAddr() const;
|
||||
void setIsAllocAddr(bool value);
|
||||
int lastAllocAddr() const;
|
||||
void setLastAllocAddr(int value);
|
||||
|
||||
void selectDevice(int address);
|
||||
|
||||
bool writeAddr(int addr, int newAddr);
|
||||
bool removeAddr(int addr);
|
||||
bool removeAllAddr();
|
||||
|
||||
std::vector<int> searchAddr(int addr = 63);
|
||||
std::vector<int> searchAddrRange(int start = 0, int end = 63);
|
||||
void stopSearch();
|
||||
|
||||
bool compareSingleAddress(int typ, int addr);
|
||||
std::pair<int, int> precompareNew(int typ, int m = 0);
|
||||
int compareAddress(int typ);
|
||||
int compareAddressNew(int typ, int m = 0);
|
||||
DaliCompareAddrResult compareAddr(int ad, std::optional<int> minH, std::optional<int> minM,
|
||||
std::optional<int> minL);
|
||||
int compareMulti(int h, int m, int l, int ad);
|
||||
bool allocateAllAddr(int ads = 0);
|
||||
void stopAllocAddr();
|
||||
|
||||
bool removeFromScene(int addr, int scene);
|
||||
std::optional<int> getSceneBright(int addr, int scene);
|
||||
bool resetAndAllocAddr(int n = 0, bool removeAddrFirst = false, bool closeLight = false);
|
||||
|
||||
private:
|
||||
DaliBase& base_;
|
||||
};
|
||||
166
include/base.hpp
Normal file
166
include/base.hpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
|
||||
#include "dali_comm.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
struct DaliStatus {
|
||||
bool controlGearPresent = false;
|
||||
bool lampFailure = false;
|
||||
bool lampPowerOn = false;
|
||||
bool limitError = false;
|
||||
bool fadingCompleted = false;
|
||||
bool resetState = false;
|
||||
bool missingShortAddress = false;
|
||||
bool psFault = false;
|
||||
|
||||
static DaliStatus fromByte(uint8_t status);
|
||||
};
|
||||
|
||||
class DaliBase {
|
||||
public:
|
||||
explicit DaliBase(DaliComm& comm);
|
||||
|
||||
// Runtime state mirrored from Dart DaliBase.
|
||||
const int broadcast = 127;
|
||||
bool isAllocAddr = false;
|
||||
int lastAllocAddr = 0;
|
||||
int selectedAddress = 127;
|
||||
|
||||
int64_t mcuTicks() const;
|
||||
|
||||
// Scene helpers
|
||||
bool toScene(int a, int s);
|
||||
bool reset(int a, int t = 2);
|
||||
|
||||
// Brightness helpers
|
||||
int brightnessToLog(int brightness) const;
|
||||
int logToBrightness(int logValue) const;
|
||||
bool setBright(int a, int b);
|
||||
bool setBrightPercentage(int a, double b);
|
||||
bool stopFade(int a);
|
||||
bool off(int a);
|
||||
bool on(int a);
|
||||
bool recallMaxLevel(int a);
|
||||
bool recallMinLevel(int a);
|
||||
|
||||
int groupToAddr(int gp) const;
|
||||
|
||||
// Core sending wrappers
|
||||
bool send(int a, uint8_t cmd);
|
||||
bool sendCmd(uint8_t addr, uint8_t cmd);
|
||||
bool sendExtCmd(int cmd, int value);
|
||||
|
||||
// DTR helpers
|
||||
bool setDTR(int value);
|
||||
bool setDTR1(int value);
|
||||
bool setDTR2(int value);
|
||||
std::optional<int> getDTR(int a);
|
||||
std::optional<int> getDTR1(int a);
|
||||
std::optional<int> getDTR2(int a);
|
||||
bool copyCurrentBrightToDTR(int a);
|
||||
|
||||
// Colour value helpers (DT8 plumbing)
|
||||
bool queryColourValue(int a);
|
||||
bool storeDTRAsAddr(int a);
|
||||
bool storeDTRAsSceneBright(int a, int s);
|
||||
bool storeScene(int a, int s);
|
||||
bool removeScene(int a, int s);
|
||||
bool addToGroup(int a, int g);
|
||||
bool removeFromGroup(int a, int g);
|
||||
bool storeDTRAsFadeTime(int a);
|
||||
bool storeDTRAsFadeRate(int a);
|
||||
bool storeDTRAsPoweredBright(int a);
|
||||
bool storeDTRAsSystemFailureLevel(int a);
|
||||
bool storeDTRAsMinLevel(int a);
|
||||
bool storeDTRAsMaxLevel(int a);
|
||||
bool storeColourTempLimits(int a);
|
||||
|
||||
std::optional<bool> getOnlineStatus(int a);
|
||||
std::optional<int> getBright(int a);
|
||||
std::optional<int> getDeviceType(int a);
|
||||
std::optional<int> getPhysicalMinLevel(int a);
|
||||
std::optional<int> getDeviceVersion(int a);
|
||||
|
||||
bool dtSelect(int value);
|
||||
bool activate(int a);
|
||||
bool setDTRAsColourX(int a);
|
||||
bool setDTRAsColourY(int a);
|
||||
bool setDTRAsColourRGB(int a);
|
||||
bool setDTRAsColourTemp(int a);
|
||||
bool copyReportColourToTemp(int a);
|
||||
|
||||
bool setGradualChangeSpeed(int a, int value);
|
||||
bool setGradualChangeRate(int a, int value);
|
||||
std::optional<std::pair<int, int>> getGradualChange(int a);
|
||||
std::optional<int> getGradualChangeRate(int a);
|
||||
std::optional<int> getGradualChangeSpeed(int a);
|
||||
|
||||
bool setPowerOnLevel(int a, int value);
|
||||
std::optional<int> getPowerOnLevel(int a);
|
||||
bool setSystemFailureLevel(int a, int value);
|
||||
std::optional<int> getSystemFailureLevel(int a);
|
||||
bool setMinLevel(int a, int value);
|
||||
std::optional<int> getMinLevel(int a);
|
||||
bool setMaxLevel(int a, int value);
|
||||
std::optional<int> getMaxLevel(int a);
|
||||
bool setFadeTime(int a, int value);
|
||||
std::optional<int> getFadeTime(int a);
|
||||
bool setFadeRate(int a, int value);
|
||||
std::optional<int> getFadeRate(int a);
|
||||
std::optional<int> getNextDeviceType(int a);
|
||||
|
||||
std::optional<int> getGroupH(int a);
|
||||
std::optional<int> getGroupL(int a);
|
||||
std::optional<int> getGroup(int a);
|
||||
bool setGroup(int a, int value);
|
||||
|
||||
std::optional<int> getScene(int a, int b);
|
||||
bool setScene(int a, int b);
|
||||
std::map<int, int> getScenes(int a);
|
||||
|
||||
std::optional<int> getStatus(int a);
|
||||
std::optional<bool> getControlGearPresent(int a);
|
||||
std::optional<bool> getLampFailureStatus(int a);
|
||||
std::optional<bool> getLampPowerOnStatus(int a);
|
||||
std::optional<bool> getLimitError(int a);
|
||||
std::optional<bool> getResetStatus(int a);
|
||||
std::optional<bool> getMissingShortAddress(int a);
|
||||
|
||||
bool terminate();
|
||||
bool randomise();
|
||||
bool initialiseAll();
|
||||
bool initialise();
|
||||
bool withdraw();
|
||||
bool cancel();
|
||||
bool physicalSelection();
|
||||
|
||||
bool queryAddressH(int addr);
|
||||
bool queryAddressM(int addr);
|
||||
bool queryAddressL(int addr);
|
||||
bool programShortAddr(int a);
|
||||
std::optional<int> queryShortAddr();
|
||||
std::optional<bool> verifyShortAddr(int a);
|
||||
std::optional<bool> compareAddress();
|
||||
std::optional<bool> compare(int h, int m, int l);
|
||||
|
||||
std::optional<int> getRandomAddrH(int addr);
|
||||
std::optional<int> getRandomAddrM(int addr);
|
||||
std::optional<int> getRandomAddrL(int addr);
|
||||
|
||||
// Exposed query helpers mirroring Dart API.
|
||||
std::optional<int> query(int a, uint8_t cmd);
|
||||
std::optional<int> queryCmd(uint8_t addr, uint8_t cmd);
|
||||
|
||||
private:
|
||||
DaliComm& comm_;
|
||||
|
||||
static uint8_t encodeCmdAddr(int dec_addr);
|
||||
static uint8_t encodeArcAddr(int dec_addr);
|
||||
};
|
||||
71
include/bus_monitor.hpp
Normal file
71
include/bus_monitor.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include "decode.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
enum class BusDir { front, back };
|
||||
|
||||
struct BusFrame {
|
||||
BusDir dir = BusDir::front;
|
||||
int proto = 0x10;
|
||||
int b1 = 0;
|
||||
int b2 = 0;
|
||||
};
|
||||
|
||||
class BusMonitor {
|
||||
public:
|
||||
static BusMonitor& I() {
|
||||
static BusMonitor inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
void setResponseWindowMs(int ms) { decoder.responseWindowMs = ms; }
|
||||
|
||||
void setRawSink(std::function<void(const BusFrame&)> sink) { rawSink_ = std::move(sink); }
|
||||
void setDecodedSink(std::function<void(const DecodedRecord&)> sink) { decodedSink_ = std::move(sink); }
|
||||
|
||||
void emitFront(int proto, int addr, int cmd) {
|
||||
BusFrame f{BusDir::front, proto, addr & 0xFF, cmd & 0xFF};
|
||||
rawFrames.push_back(f);
|
||||
if (rawSink_) rawSink_(f);
|
||||
|
||||
auto rec = decoder.decode(addr & 0xFF, cmd & 0xFF, proto);
|
||||
records.push_back(rec);
|
||||
if (decodedSink_) decodedSink_(rec);
|
||||
}
|
||||
|
||||
void emitBack(int value, int prefix = 0xFF) {
|
||||
BusFrame f{BusDir::back, 0xFF, prefix & 0xFF, value & 0xFF};
|
||||
rawFrames.push_back(f);
|
||||
if (rawSink_) rawSink_(f);
|
||||
|
||||
auto rec = decoder.decodeCmdResponse(value & 0xFF, prefix & 0xFF);
|
||||
records.push_back(rec);
|
||||
if (decodedSink_) decodedSink_(rec);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
rawFrames.clear();
|
||||
records.clear();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
rawFrames.clear();
|
||||
records.clear();
|
||||
rawSink_ = nullptr;
|
||||
decodedSink_ = nullptr;
|
||||
}
|
||||
|
||||
DaliDecode decoder;
|
||||
std::vector<BusFrame> rawFrames;
|
||||
std::vector<DecodedRecord> records;
|
||||
|
||||
private:
|
||||
BusMonitor() = default;
|
||||
|
||||
std::function<void(const BusFrame&)> rawSink_;
|
||||
std::function<void(const DecodedRecord&)> decodedSink_;
|
||||
};
|
||||
30
include/color.hpp
Normal file
30
include/color.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
// Colour conversion utilities mirroring lib/dali/color.dart.
|
||||
class DaliColor {
|
||||
public:
|
||||
static std::array<int, 4> toIntList(double a, double r, double g, double b);
|
||||
static int toInt(double a, double r, double g, double b);
|
||||
static double decimalRound(int num, double idp);
|
||||
static std::array<double, 3> gammaCorrection(double r, double g, double b, double gamma = 2.8);
|
||||
|
||||
static std::array<double, 3> rgb2xyz(double r, double g, double b);
|
||||
static std::array<int, 3> xyz2rgb(double x, double y, double z);
|
||||
static std::array<double, 2> xyz2xy(double x, double y, double z);
|
||||
static std::array<double, 3> xy2xyz(double xVal, double yVal);
|
||||
static std::array<double, 2> rgb2xy(double r, double g, double b);
|
||||
static std::array<int, 3> xy2rgb(double xVal, double yVal);
|
||||
|
||||
static std::array<double, 3> xyz2lab(double x, double y, double z);
|
||||
static std::array<double, 3> rgb2lab(double r, double g, double b);
|
||||
static std::array<double, 3> lab2xyz(double l, double a, double b);
|
||||
static std::array<int, 3> lab2rgb(double l, double a, double b);
|
||||
|
||||
private:
|
||||
static double srgbToLinear(double value);
|
||||
static double linearToSrgb(double value);
|
||||
};
|
||||
3
include/comm.hpp
Normal file
3
include/comm.hpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "dali_comm.hpp"
|
||||
53
include/dali.hpp
Normal file
53
include/dali.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
#include "bus_monitor.hpp"
|
||||
#include "comm.hpp"
|
||||
#include "dali_comm.hpp"
|
||||
#include "decode.hpp"
|
||||
#include "device.hpp"
|
||||
#include "dt1.hpp"
|
||||
#include "dt8.hpp"
|
||||
#include "addr.hpp"
|
||||
#include "color.hpp"
|
||||
#include "errors.hpp"
|
||||
#include "log.hpp"
|
||||
#include "query_scheduler.hpp"
|
||||
#include "sequence.hpp"
|
||||
#include "sequence_store.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// Convenience umbrella header for the ESP-IDF DALI component.
|
||||
|
||||
class Dali {
|
||||
public:
|
||||
static Dali& instance(DaliComm& comm) {
|
||||
if (!instance_) {
|
||||
instance_ = std::unique_ptr<Dali>(new Dali(comm));
|
||||
}
|
||||
return *instance_;
|
||||
}
|
||||
|
||||
static void resetInstance() { instance_.reset(); }
|
||||
|
||||
static constexpr int broadcast = 127;
|
||||
|
||||
std::string name = "dali1";
|
||||
int gw = 0;
|
||||
DaliBase base;
|
||||
DaliDecode decode;
|
||||
DaliDT1 dt1;
|
||||
DaliDT8 dt8;
|
||||
DaliAddr addr;
|
||||
|
||||
explicit Dali(DaliComm& comm, int g = 0, const std::string& n = "dali1")
|
||||
: name(n), gw(g), base(comm), decode(), dt1(base), dt8(base), addr(base) {}
|
||||
|
||||
void open() {}
|
||||
void close() {}
|
||||
|
||||
private:
|
||||
inline static std::unique_ptr<Dali> instance_ = nullptr;
|
||||
};
|
||||
68
include/dali_comm.hpp
Normal file
68
include/dali_comm.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
// Lightweight communicator for DALI gateway type 1 (USB UART new frame format).
|
||||
// Frames:
|
||||
// - Send: [0x10, addr, cmd]
|
||||
// - Ext: [0x11, addr, cmd] (sent twice with a small delay)
|
||||
// - Query: [0x12, addr, cmd] -> expects [0xFF, data] on success
|
||||
// Callers provide UART callbacks; this class only builds frames and parses basic responses.
|
||||
class DaliComm {
|
||||
public:
|
||||
using SendCallback = std::function<bool(const uint8_t* data, size_t len)>;
|
||||
using ReadCallback = std::function<std::vector<uint8_t>(size_t len, uint32_t timeout_ms)>;
|
||||
using TransactCallback = std::function<std::vector<uint8_t>(const uint8_t* data, size_t len)>;
|
||||
using DelayCallback = std::function<void(uint32_t ms)>;
|
||||
|
||||
explicit DaliComm(SendCallback send_cb,
|
||||
ReadCallback read_cb = nullptr,
|
||||
TransactCallback transact_cb = nullptr,
|
||||
DelayCallback delay_cb = nullptr);
|
||||
|
||||
void setSendCallback(SendCallback cb);
|
||||
void setReadCallback(ReadCallback cb);
|
||||
void setTransactCallback(TransactCallback cb);
|
||||
void setDelayCallback(DelayCallback cb);
|
||||
|
||||
// Methods mirroring lib/dali/comm.dart naming where practical.
|
||||
static std::vector<uint8_t> checksum(const std::vector<uint8_t>& data);
|
||||
bool write(const std::vector<uint8_t>& data) const;
|
||||
std::vector<uint8_t> read(size_t len, uint32_t timeout_ms = 100) const;
|
||||
int checkGatewayType(int gateway) const;
|
||||
void flush() const;
|
||||
bool resetBus() const;
|
||||
bool sendRaw(uint8_t addr, uint8_t cmd) const;
|
||||
bool sendRawNew(uint8_t addr, uint8_t cmd, bool needVerify = false) const;
|
||||
bool sendExtRaw(uint8_t addr, uint8_t cmd) const;
|
||||
bool sendExtRawNew(uint8_t addr, uint8_t cmd) const;
|
||||
std::optional<uint8_t> queryRaw(uint8_t addr, uint8_t cmd) const;
|
||||
std::optional<uint8_t> queryRawNew(uint8_t addr, uint8_t cmd) const;
|
||||
bool send(int dec_addr, uint8_t cmd) const;
|
||||
std::optional<uint8_t> query(int dec_addr, uint8_t cmd) const;
|
||||
bool getBusStatus() const;
|
||||
|
||||
// Send standard command frame (0x10).
|
||||
bool sendCmd(uint8_t addr, uint8_t cmd) const;
|
||||
// Send extended command frame (0x11).
|
||||
bool sendExtCmd(uint8_t addr, uint8_t cmd) const;
|
||||
// Send query frame (0x12) and parse single-byte response. Returns nullopt on no/invalid response.
|
||||
std::optional<uint8_t> queryCmd(uint8_t addr, uint8_t cmd) const;
|
||||
|
||||
// Helpers to mirror Dart address conversion (DEC short address -> DALI odd/even encoded).
|
||||
static uint8_t toCmdAddr(int dec_addr); // odd address for commands
|
||||
static uint8_t toArcAddr(int dec_addr); // even address for direct arc
|
||||
|
||||
private:
|
||||
SendCallback send_;
|
||||
ReadCallback read_;
|
||||
TransactCallback transact_;
|
||||
DelayCallback delay_;
|
||||
|
||||
bool writeFrame(const std::vector<uint8_t>& frame) const;
|
||||
void sleepMs(uint32_t ms) const;
|
||||
};
|
||||
157
include/dali_define.hpp
Normal file
157
include/dali_define.hpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#pragma once
|
||||
|
||||
// Standard control commands (IEC 62386 command set)
|
||||
#define DALI_CMD_OFF 0x00
|
||||
#define DALI_CMD_RECALL_MAX 0x05
|
||||
#define DALI_CMD_RECALL_MAX_LEVEL DALI_CMD_RECALL_MAX
|
||||
#define DALI_CMD_RECALL_MIN 0x06
|
||||
#define DALI_CMD_RECALL_MIN_LEVEL DALI_CMD_RECALL_MIN
|
||||
#define DALI_CMD_RESET 0x20
|
||||
#define DALI_CMD_STORE_ACTUAL_LEVEL_IN_THE_DTR 0x21
|
||||
#define DALI_CMD_STORE_THE_DTR_AS_MAX_LEVEL 0x2A
|
||||
#define DALI_CMD_STORE_THE_DTR_AS_MIN_LEVEL 0x2B
|
||||
#define DALI_CMD_STORE_THE_DTR_AS_SYS_FAIL_LEVEL 0x2C
|
||||
#define DALI_CMD_STORE_THE_DTR_AS_PWR_ON_LEVEL 0x2D
|
||||
#define DALI_CMD_STORE_THE_DTR_AS_FADE_TIME 0x2E
|
||||
#define DALI_CMD_STORE_THE_DTR_AS_FADE_RATE 0x2F
|
||||
#define DALI_CMD_STORE_DTR_AS_SHORT_ADDRESS 0x80
|
||||
#define DALI_CMD_STOP_FADE 0xFF
|
||||
|
||||
// Indexed command ranges
|
||||
#define DALI_CMD_GO_TO_SCENE(scene) (0x10 + (scene))
|
||||
#define DALI_CMD_SET_SCENE(scene) (0x40 + (scene))
|
||||
#define DALI_CMD_REMOVE_SCENE(scene) (0x50 + (scene))
|
||||
#define DALI_CMD_ADD_TO_GROUP(group) (0x60 + (group))
|
||||
#define DALI_CMD_REMOVE_FROM_GROUP(group) (0x70 + (group))
|
||||
#define DALI_CMD_QUERY_SCENE_LEVEL(scene) (0xB0 + (scene))
|
||||
|
||||
// Command range boundaries used by decoders
|
||||
#define DALI_CMD_GO_TO_SCENE_MIN DALI_CMD_GO_TO_SCENE(0)
|
||||
#define DALI_CMD_GO_TO_SCENE_MAX DALI_CMD_GO_TO_SCENE(15)
|
||||
#define DALI_CMD_SET_SCENE_MIN DALI_CMD_SET_SCENE(0)
|
||||
#define DALI_CMD_SET_SCENE_MAX DALI_CMD_SET_SCENE(15)
|
||||
#define DALI_CMD_ADD_TO_GROUP_MIN DALI_CMD_ADD_TO_GROUP(0)
|
||||
#define DALI_CMD_ADD_TO_GROUP_MAX DALI_CMD_ADD_TO_GROUP(15)
|
||||
#define DALI_CMD_REMOVE_FROM_GROUP_MIN DALI_CMD_REMOVE_FROM_GROUP(0)
|
||||
#define DALI_CMD_REMOVE_FROM_GROUP_MAX DALI_CMD_REMOVE_FROM_GROUP(15)
|
||||
#define DALI_CMD_QUERY_SCENE_LEVEL_MIN DALI_CMD_QUERY_SCENE_LEVEL(0)
|
||||
#define DALI_CMD_QUERY_SCENE_LEVEL_MAX DALI_CMD_QUERY_SCENE_LEVEL(15)
|
||||
#define DALI_CMD_SPECIAL_RANGE_MIN DALI_CMD_QUERY_STATUS
|
||||
#define DALI_CMD_SPECIAL_RANGE_MAX DALI_CMD_DT8_QUERY_ASSIGNED_COLOR
|
||||
|
||||
// Query commands
|
||||
#define DALI_CMD_QUERY_STATUS 0x90
|
||||
#define DALI_CMD_QUERY_BALLAST 0x91
|
||||
#define DALI_CMD_QUERY_LAMP_FAILURE 0x92
|
||||
#define DALI_CMD_QUERY_LAMP_POWER_ON 0x93
|
||||
#define DALI_CMD_QUERY_LIMIT_ERROR 0x94
|
||||
#define DALI_CMD_QUERY_RESET_STATE 0x95
|
||||
#define DALI_CMD_QUERY_MISSING_SHORT_ADDRESS 0x96
|
||||
#define DALI_CMD_QUERY_VERSION_NUMBER 0x97
|
||||
#define DALI_CMD_QUERY_CONTENT_DTR 0x98
|
||||
#define DALI_CMD_QUERY_DEVICE_TYPE 0x99
|
||||
#define DALI_CMD_QUERY_PHYSICAL_MINIMUM_LEVEL 0x9A
|
||||
#define DALI_CMD_QUERY_POWER_FAILURE 0x9B
|
||||
#define DALI_CMD_QUERY_CONTENT_DTR1 0x9C
|
||||
#define DALI_CMD_QUERY_CONTENT_DTR2 0x9D
|
||||
#define DALI_CMD_QUERY_OPERATING_MODE 0x9E
|
||||
#define DALI_CMD_QUERY_LIGHT_SOURCE_TYPE 0x9F
|
||||
#define DALI_CMD_QUERY_ACTUAL_LEVEL 0xA0
|
||||
#define DALI_CMD_QUERY_MAX_LEVEL 0xA1
|
||||
#define DALI_CMD_QUERY_MIN_LEVEL 0xA2
|
||||
#define DALI_CMD_QUERY_POWER_ON_LEVEL 0xA3
|
||||
#define DALI_CMD_QUERY_SYSTEM_FAILURE_LEVEL 0xA4
|
||||
#define DALI_CMD_QUERY_FADE_TIME_FADE_RATE 0xA5
|
||||
#define DALI_CMD_QUERY_MANUFACTURER_SPECIFIC_MODE 0xA6
|
||||
#define DALI_CMD_QUERY_NEXT_DEVICE_TYPE 0xA7
|
||||
#define DALI_CMD_QUERY_EXTENDED_FADE_TIME 0xA8
|
||||
#define DALI_CMD_QUERY_CONTROL_GEAR_FAILURE 0xAA
|
||||
#define DALI_CMD_QUERY_GROUPS_0_7 0xC0
|
||||
#define DALI_CMD_QUERY_GROUP_8_15 0xC1
|
||||
#define DALI_CMD_QUERY_RANDOM_ADDRESS_H 0xC2
|
||||
#define DALI_CMD_QUERY_RANDOM_ADDRESS_M 0xC3
|
||||
#define DALI_CMD_QUERY_RANDOM_ADDRESS_L 0xC4
|
||||
#define DALI_CMD_READ_MEMORY_LOCATION 0xC5
|
||||
|
||||
// Special/programming commands
|
||||
#define DALI_CMD_SPECIAL_TERMINATE 0xA1
|
||||
#define DALI_CMD_SPECIAL_SET_DTR0 0xA3
|
||||
#define DALI_CMD_SPECIAL_INITIALIZE 0xA5
|
||||
#define DALI_CMD_SPECIAL_RANDOMIZE 0xA7
|
||||
#define DALI_CMD_SPECIAL_COMPARE 0xA9
|
||||
#define DALI_CMD_SPECIAL_WITHDRAW 0xAB
|
||||
#define DALI_CMD_SPECIAL_CANCEL 0xAD
|
||||
#define DALI_CMD_SPECIAL_SEARCHADDRH 0xB1
|
||||
#define DALI_CMD_SPECIAL_SEARCHADDRM 0xB3
|
||||
#define DALI_CMD_SPECIAL_SEARCHADDRL 0xB5
|
||||
#define DALI_CMD_SPECIAL_PROGRAM_SHORT_ADDRESS 0xB7
|
||||
#define DALI_CMD_SPECIAL_VERIFY_SHORT_ADDRESS 0xB9
|
||||
#define DALI_CMD_SPECIAL_QUERY_SHORT_ADDRESS 0xBB
|
||||
#define DALI_CMD_SPECIAL_PHYSICAL_SELECTION 0xBD
|
||||
#define DALI_CMD_SPECIAL_DT_SELECT 0xC1
|
||||
#define DALI_CMD_SPECIAL_SET_DTR_1 0xC3
|
||||
#define DALI_CMD_SPECIAL_SET_DTR_2 0xC5
|
||||
|
||||
// DT8 commands and queries
|
||||
#define DALI_CMD_DT8_STORE_DTR_AS_COLORX 0xE0
|
||||
#define DALI_CMD_DT8_STORE_DTR_AS_COLORY 0xE1
|
||||
#define DALI_CMD_DT8_ACTIVATE 0xE2
|
||||
#define DALI_CMD_DT8_STEP_UP_X_COORDINATE 0xE3
|
||||
#define DALI_CMD_DT8_STEP_DOWN_X_COORDINATE 0xE4
|
||||
#define DALI_CMD_DT8_STEP_UP_Y_COORDINATE 0xE5
|
||||
#define DALI_CMD_DT8_STEP_DOWN_Y_COORDINATE 0xE6
|
||||
#define DALI_CMD_DT8_SET_COLOR_TEMPERATURE 0xE7
|
||||
#define DALI_CMD_DT8_STEP_UP_COLOR_TEMPERATURE 0xE8
|
||||
#define DALI_CMD_DT8_STEP_DOWN_COLOR_TEMPERATURE 0xE9
|
||||
#define DALI_CMD_DT8_SET_TEMPORARY_PRIMARY_DIM_LEVEL 0xEA
|
||||
#define DALI_CMD_DT8_SET_TEMPORARY_RGB_DIM_LEVELS 0xEB
|
||||
#define DALI_CMD_DT8_SET_TEMPORARY_WAF_DIM_LEVELS 0xEC
|
||||
#define DALI_CMD_DT8_SET_TEMPORARY_RGBWAF_CONTROL 0xED
|
||||
#define DALI_CMD_DT8_COPY_REPORT_TO_TEMPORARY 0xEE
|
||||
#define DALI_CMD_DT8_STORE_PRIMARY_N_TY 0xF0
|
||||
#define DALI_CMD_DT8_STORE_PRIMARY_N_XY 0xF1
|
||||
#define DALI_CMD_DT8_STORE_COLOR_TEMPERATURE_LIMIT 0xF2
|
||||
#define DALI_CMD_DT8_SET_GEAR_FEATURES 0xF3
|
||||
#define DALI_CMD_DT8_ASSIGN_COLOR_TO_LINKED_CHANNEL 0xF5
|
||||
#define DALI_CMD_DT8_START_AUTO_CALIBRATION 0xF6
|
||||
#define DALI_CMD_DT8_QUERY_GEAR_FEATURES_STATUS 0xF7
|
||||
#define DALI_CMD_QUERY_COLOR_STATUS 0xF8
|
||||
#define DALI_CMD_QUERY_COLOR_TYPE 0xF9
|
||||
#define DALI_CMD_QUERY_COLOR_VALUE 0xFA
|
||||
#define DALI_CMD_DT8_QUERY_RGBWAF_CONTROL 0xFB
|
||||
#define DALI_CMD_DT8_QUERY_ASSIGNED_COLOR 0xFC
|
||||
#define DALI_CMD_DT8_QUERY_EXTENDED_VERSION 0xFF
|
||||
|
||||
// DT1 commands and queries
|
||||
#define DALI_CMD_DT1_REST 0xE0
|
||||
#define DALI_CMD_DT1_INHIBIT 0xE1
|
||||
#define DALI_CMD_DT1_RE_LIGHT_RESET_INHIBIT 0xE2
|
||||
#define DALI_CMD_DT1_START_FUNCTION_TEST 0xE3
|
||||
#define DALI_CMD_DT1_START_DURATION_TEST 0xE4
|
||||
#define DALI_CMD_DT1_STOP_TEST 0xE5
|
||||
#define DALI_CMD_DT1_RESET_FUNCTION_TEST_DONE_FLAG 0xE6
|
||||
#define DALI_CMD_DT1_RESET_DURATION_TEST_DONE_FLAG 0xE7
|
||||
#define DALI_CMD_DT1_RESET_LAMP_TIME 0xE8
|
||||
#define DALI_CMD_DT1_STORE_DTR_AS_EMERGENCY_LEVEL 0xE9
|
||||
#define DALI_CMD_DT1_STORE_DTR_AS_DELAY_TIME_HIGH 0xEA
|
||||
#define DALI_CMD_DT1_STORE_DTR_AS_DELAY_TIME_LOW 0xEB
|
||||
#define DALI_CMD_DT1_STORE_DTR_AS_PROLONG_TIME 0xEC
|
||||
#define DALI_CMD_DT1_STORE_DTR_AS_RATED_DURATION 0xED
|
||||
#define DALI_CMD_DT1_STORE_DTR_AS_EMERGENCY_MIN_LEVEL 0xEE
|
||||
#define DALI_CMD_DT1_STORE_DTR_AS_EMERGENCY_MAX_LEVEL 0xEF
|
||||
#define DALI_CMD_DT1_START_IDENTIFICATION 0xF0
|
||||
#define DALI_CMD_DT1_QUERY_EMERGENCY_LEVEL 0xF1
|
||||
#define DALI_CMD_DT1_QUERY_EMERGENCY_MIN_LEVEL 0xF2
|
||||
#define DALI_CMD_DT1_QUERY_EMERGENCY_MAX_LEVEL 0xF3
|
||||
#define DALI_CMD_DT1_QUERY_PROLONG_TIME 0xF4
|
||||
#define DALI_CMD_DT1_QUERY_FUNCTION_TEST_INTERVAL 0xF5
|
||||
#define DALI_CMD_DT1_QUERY_DURATION_TEST_INTERVAL 0xF6
|
||||
#define DALI_CMD_DT1_QUERY_DURATION_TEST_RESULT 0xF7
|
||||
#define DALI_CMD_DT1_QUERY_LAMP_EMERGENCY_TIME 0xF8
|
||||
#define DALI_CMD_DT1_QUERY_RATED_DURATION 0xF9
|
||||
#define DALI_CMD_DT1_QUERY_EMERGENCY_MODE 0xFA
|
||||
#define DALI_CMD_DT1_QUERY_FEATURE 0xFB
|
||||
#define DALI_CMD_DT1_QUERY_FAILURE_STATUS 0xFC
|
||||
#define DALI_CMD_DT1_QUERY_STATUS 0xFD
|
||||
#define DALI_CMD_DT1_PERFORM_DTR_SELECTED_FUNCTION 0xFE
|
||||
#define DALI_CMD_DT1_QUERY_EXTENDED_VERSION 0xFF
|
||||
56
include/decode.hpp
Normal file
56
include/decode.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct DecodedRecord {
|
||||
std::string text;
|
||||
std::string type;
|
||||
int addr = -1;
|
||||
int cmd = -1;
|
||||
int proto = -1;
|
||||
};
|
||||
|
||||
class DaliDecode {
|
||||
public:
|
||||
bool displayRaw = true;
|
||||
int responseWindowMs = 100;
|
||||
|
||||
int dtr0 = 0;
|
||||
int dtr1 = 0;
|
||||
int dtr2 = 0;
|
||||
|
||||
DaliDecode();
|
||||
|
||||
int isQueryCmd(int cmd) const;
|
||||
|
||||
DecodedRecord decodeBright(int addr, int level);
|
||||
DecodedRecord decodeScene(int addr, int sceneCmd);
|
||||
DecodedRecord decodeCmd(int addr, int c);
|
||||
DecodedRecord decodeSpCmd(int addr, int c);
|
||||
DecodedRecord querySpCMD(int cmdByte, int dataByte);
|
||||
DecodedRecord decodeQuery(int addr, int c);
|
||||
DecodedRecord decodeCmdResponse(int value, int gwPrefix = 0xFF);
|
||||
DecodedRecord decode(int addr, int c, int proto = 0x10);
|
||||
|
||||
private:
|
||||
std::map<int, std::string> cmd_;
|
||||
std::map<int, std::string> sCMD_;
|
||||
std::vector<int> queryCmd_;
|
||||
|
||||
int lastQueryCmd_ = 0;
|
||||
int64_t lastQueryAtMs_ = 0;
|
||||
int pendingColourType_ = -1;
|
||||
int64_t lastColourQueryAtMs_ = 0;
|
||||
|
||||
static int64_t nowMs();
|
||||
static std::string hex(int v);
|
||||
static std::string bin(int v);
|
||||
static std::string yn(bool b);
|
||||
static std::string whoLabel(int addr, bool even);
|
||||
static std::string deviceTypeName(int v);
|
||||
static std::string lightSourceName(int v);
|
||||
DecodedRecord withRaw(const DecodedRecord& r) const;
|
||||
};
|
||||
116
include/device.hpp
Normal file
116
include/device.hpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
|
||||
#include "model_value.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct DaliLongAddress {
|
||||
int h = 0;
|
||||
int m = 0;
|
||||
int l = 0;
|
||||
|
||||
static DaliLongAddress fromJson(const DaliValue::Object* json);
|
||||
DaliValue::Object toJson() const;
|
||||
};
|
||||
|
||||
struct DaliDeviceCapabilities {
|
||||
std::optional<bool> supportsDt1;
|
||||
std::optional<bool> supportsDt8;
|
||||
|
||||
static DaliDeviceCapabilities fromJson(const DaliValue::Object* json);
|
||||
DaliValue::Object toJson() const;
|
||||
void merge(const DaliDeviceCapabilities& other);
|
||||
};
|
||||
|
||||
struct DaliStatusFlags {
|
||||
std::optional<bool> controlGearPresent;
|
||||
std::optional<bool> lampFailure;
|
||||
std::optional<bool> lampPowerOn;
|
||||
std::optional<bool> limitError;
|
||||
std::optional<bool> fadingCompleted;
|
||||
std::optional<bool> resetState;
|
||||
std::optional<bool> missingShortAddress;
|
||||
std::optional<bool> psFault;
|
||||
|
||||
static DaliStatusFlags fromJson(const DaliValue::Object* json);
|
||||
DaliValue::Object toJson() const;
|
||||
bool hasData() const;
|
||||
void merge(const DaliStatusFlags& other);
|
||||
};
|
||||
|
||||
struct DaliDt8State {
|
||||
std::optional<int> colorType;
|
||||
std::optional<std::string> activeMode;
|
||||
std::optional<int> xyX;
|
||||
std::optional<int> xyY;
|
||||
std::optional<int> xyMinX;
|
||||
std::optional<int> xyMaxX;
|
||||
std::optional<int> xyMinY;
|
||||
std::optional<int> xyMaxY;
|
||||
std::optional<int> mirek;
|
||||
std::optional<int> mirekMin;
|
||||
std::optional<int> mirekMax;
|
||||
std::optional<std::vector<int>> rgbwaf;
|
||||
std::optional<std::vector<int>> primaryN;
|
||||
|
||||
static DaliDt8State fromJson(const DaliValue::Object* json);
|
||||
DaliValue::Object toJson() const;
|
||||
};
|
||||
|
||||
struct DaliDt1State {
|
||||
std::optional<int> emergencyLevel;
|
||||
std::optional<int> emergencyMinLevel;
|
||||
std::optional<int> emergencyMaxLevel;
|
||||
std::optional<int> prolongTimeMinutes;
|
||||
std::optional<int> ratedDurationMinutes;
|
||||
std::optional<int> testDelayTime;
|
||||
std::optional<int> failureStatus;
|
||||
std::optional<int> emergencyStatus;
|
||||
std::optional<int> emergencyMode;
|
||||
std::optional<int> feature;
|
||||
std::optional<int> version;
|
||||
|
||||
static DaliDt1State fromJson(const DaliValue::Object* json);
|
||||
DaliValue::Object toJson() const;
|
||||
};
|
||||
|
||||
struct DaliDevice {
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::optional<int> shortAddress;
|
||||
std::optional<DaliLongAddress> longAddress;
|
||||
bool isolated = false;
|
||||
|
||||
std::optional<int> brightness;
|
||||
std::optional<int> groupBits;
|
||||
std::optional<std::vector<int>> scenes;
|
||||
|
||||
std::optional<int> fadeTime;
|
||||
std::optional<int> fadeRate;
|
||||
std::optional<int> powerOnLevel;
|
||||
std::optional<int> systemFailureLevel;
|
||||
std::optional<int> minLevel;
|
||||
std::optional<int> maxLevel;
|
||||
std::optional<int> operatingMode;
|
||||
std::optional<int> physicalMinLevel;
|
||||
|
||||
std::optional<int> deviceType;
|
||||
std::vector<int> extType;
|
||||
std::optional<int> version;
|
||||
|
||||
DaliDeviceCapabilities capabilities;
|
||||
std::optional<DaliDt8State> dt8;
|
||||
std::optional<DaliDt1State> dt1;
|
||||
DaliStatusFlags statusFlags;
|
||||
|
||||
std::optional<std::string> lastSyncedUtc;
|
||||
DaliValue::Object metadata;
|
||||
|
||||
static DaliDevice fromJson(const DaliValue::Object& json);
|
||||
DaliValue::Object toJson() const;
|
||||
|
||||
std::string displayName() const;
|
||||
void merge(const DaliDevice& other);
|
||||
};
|
||||
129
include/dt1.hpp
Normal file
129
include/dt1.hpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
struct DT1TestStatusDetailed {
|
||||
std::optional<int> failureStatus;
|
||||
std::optional<int> emergencyStatus;
|
||||
std::optional<int> emergencyMode;
|
||||
std::optional<int> feature;
|
||||
bool testInProgress = false;
|
||||
bool lampFailure = false;
|
||||
bool batteryFailure = false;
|
||||
bool functionTestActive = false;
|
||||
bool durationTestActive = false;
|
||||
bool testDone = false;
|
||||
bool identifyActive = false;
|
||||
bool physicalSelectionActive = false;
|
||||
};
|
||||
|
||||
class DaliDT1DeviceStatus {
|
||||
public:
|
||||
explicit DaliDT1DeviceStatus(int raw) : raw_(raw & 0xFF) {}
|
||||
|
||||
int raw() const { return raw_; }
|
||||
bool controlGearFailure() const { return bit(0x01); }
|
||||
bool controlGearOk() const { return !controlGearFailure(); }
|
||||
bool lampFailure() const { return bit(0x02); }
|
||||
bool lampPoweredByEmergencyGear() const { return bit(0x04); }
|
||||
bool arcPowerBit3() const { return bit(0x08); }
|
||||
bool arcPowerBit4() const { return bit(0x10); }
|
||||
bool resetState() const { return bit(0x20); }
|
||||
bool missingShortAddress() const { return bit(0x40); }
|
||||
bool arcPowerBit7() const { return bit(0x80); }
|
||||
|
||||
private:
|
||||
int raw_ = 0;
|
||||
bool bit(int mask) const { return (raw_ & mask) != 0; }
|
||||
};
|
||||
|
||||
class DaliDT1EmergencyStatus {
|
||||
public:
|
||||
explicit DaliDT1EmergencyStatus(int raw) : raw_(raw & 0xFF) {}
|
||||
|
||||
int raw() const { return raw_; }
|
||||
bool inhibitMode() const { return bit(0x01); }
|
||||
bool functionTestResultValid() const { return bit(0x02); }
|
||||
bool durationTestResultValid() const { return bit(0x04); }
|
||||
bool batteryFullyCharged() const { return bit(0x08); }
|
||||
bool functionTestRequestPending() const { return bit(0x10); }
|
||||
bool durationTestRequestPending() const { return bit(0x20); }
|
||||
bool identificationActive() const { return bit(0x40); }
|
||||
bool physicallySelected() const { return bit(0x80); }
|
||||
bool batteryChargingInProgress() const { return !batteryFullyCharged(); }
|
||||
|
||||
private:
|
||||
int raw_ = 0;
|
||||
bool bit(int mask) const { return (raw_ & mask) != 0; }
|
||||
};
|
||||
|
||||
class DaliDT1 {
|
||||
public:
|
||||
explicit DaliDT1(DaliBase& base);
|
||||
|
||||
bool enableDT1();
|
||||
|
||||
bool startDT1Test(int a, int t = 1);
|
||||
std::optional<int> getDT1EmergencyMode(int a);
|
||||
std::optional<int> getDT1Feature(int a);
|
||||
std::optional<int> getDT1FailureStatus(int a);
|
||||
std::optional<int> getDT1Status(int a);
|
||||
std::optional<int> getDT1SelfTestStatus(int a);
|
||||
std::optional<DT1TestStatusDetailed> getDT1TestStatusDetailed(int a);
|
||||
bool performDT1Test(int a, int timeout = 5);
|
||||
|
||||
bool rest(int a);
|
||||
bool inhibit(int a);
|
||||
bool reLightOrResetInhibit(int a);
|
||||
bool startFunctionTestCmd(int a);
|
||||
bool startDurationTestCmd(int a);
|
||||
bool stopTest(int a);
|
||||
bool resetFunctionTestDoneFlag(int a);
|
||||
bool resetDurationTestDoneFlag(int a);
|
||||
bool resetLampTime(int a);
|
||||
bool resetStatusFlags(int a);
|
||||
bool resetLampOperationTime(int a);
|
||||
bool resetTestResults(int a);
|
||||
|
||||
bool storeEmergencyLevel(int a, int level);
|
||||
bool storeTestDelayTimeHighByte(int a, int highByte);
|
||||
bool storeTestDelayTimeLowByte(int a, int lowByte);
|
||||
bool storeFunctionTestIntervalDays(int a, int days);
|
||||
bool storeDurationTestIntervalWeeks(int a, int weeks);
|
||||
bool storeTestDelayTime16(int a, int quartersOfHour);
|
||||
bool storeProlongTimeMinutes(int a, int minutes);
|
||||
bool storeRatedDurationMinutes(int a, int minutes);
|
||||
bool storeEmergencyMinLevel(int a, int level);
|
||||
bool storeEmergencyMaxLevel(int a, int level);
|
||||
|
||||
bool startIdentification(int a);
|
||||
bool performDTRSelectedFunction(int a,
|
||||
const std::optional<int>& dtr0 = std::nullopt,
|
||||
const std::optional<int>& dtr1 = std::nullopt);
|
||||
std::optional<int> getExtendedVersionDT1(int a);
|
||||
|
||||
std::optional<int> getEmergencyLevel(int a);
|
||||
std::optional<int> getEmergencyMinLevel(int a);
|
||||
std::optional<int> getEmergencyMaxLevel(int a);
|
||||
std::optional<int> getProlongTimeMinutes(int a);
|
||||
std::optional<int> getFunctionTestIntervalDays(int a);
|
||||
std::optional<int> getDurationTestIntervalWeeks(int a);
|
||||
std::optional<int> getDurationTestResult(int a);
|
||||
std::optional<int> getLampEmergencyTimeMinutes(int a);
|
||||
std::optional<int> getRatedDurationMinutes(int a);
|
||||
|
||||
std::optional<DaliDT1DeviceStatus> getDeviceStatus(int a);
|
||||
std::optional<DaliDT1EmergencyStatus> getEmergencyStatusDecoded(int a);
|
||||
|
||||
private:
|
||||
DaliBase& base_;
|
||||
|
||||
bool enable();
|
||||
static int addrOf(int a);
|
||||
bool send(int a, int code);
|
||||
std::optional<int> query(int a, int code);
|
||||
};
|
||||
|
||||
156
include/dt8.hpp
Normal file
156
include/dt8.hpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
#include "color.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class ColorStatus {
|
||||
public:
|
||||
explicit ColorStatus(int status) : status_(status) {}
|
||||
|
||||
bool xyOutOfRange() const { return (status_ & 0x01) != 0; }
|
||||
bool ctOutOfRange() const { return (status_ & 0x02) != 0; }
|
||||
bool autoCalibrationActive() const { return (status_ & 0x04) != 0; }
|
||||
bool autoCalibrationSuccess() const { return (status_ & 0x08) != 0; }
|
||||
bool xyActive() const { return (status_ & 0x10) != 0; }
|
||||
bool ctActive() const { return (status_ & 0x20) != 0; }
|
||||
bool primaryNActive() const { return (status_ & 0x40) != 0; }
|
||||
bool rgbwafActive() const { return (status_ & 0x80) != 0; }
|
||||
|
||||
private:
|
||||
int status_ = 0;
|
||||
};
|
||||
|
||||
class ColorTypeFeature {
|
||||
public:
|
||||
explicit ColorTypeFeature(int features) : features_(features) {}
|
||||
|
||||
int features() const { return features_; }
|
||||
bool xyCapable() const { return (features_ & 0x01) != 0; }
|
||||
bool ctCapable() const { return (features_ & 0x02) != 0; }
|
||||
int primaryCount() const { return (features_ >> 2) & 0x07; }
|
||||
int rgbwafChannels() const { return (features_ >> 5) & 0x07; }
|
||||
bool primaryNCapable() const { return primaryCount() > 0; }
|
||||
bool rgbwafCapable() const { return rgbwafChannels() > 0; }
|
||||
|
||||
private:
|
||||
int features_ = 0;
|
||||
};
|
||||
|
||||
class DaliDT8 {
|
||||
public:
|
||||
explicit DaliDT8(DaliBase& base);
|
||||
|
||||
bool enableDT8();
|
||||
std::optional<ColorTypeFeature> getColorTypeFeature(int a);
|
||||
std::optional<ColorStatus> getColorStatus(int a);
|
||||
|
||||
std::optional<int> getColTempRaw(int a, int type = 2);
|
||||
bool setColTempRaw(int a, int value);
|
||||
bool setColorTemperature(int addr, int kelvin);
|
||||
std::optional<int> getColorTemperature(int a);
|
||||
std::optional<int> getMinColorTemperature(int a);
|
||||
std::optional<int> getMaxColorTemperature(int a);
|
||||
std::optional<int> getPhysicalMinColorTemperature(int a);
|
||||
std::optional<int> getPhysicalMaxColorTemperature(int a);
|
||||
|
||||
bool setColourRaw(int addr, int x1, int y1);
|
||||
|
||||
// Temporary setters
|
||||
bool setTemporaryColourXRaw(int addr, int x1);
|
||||
bool setTemporaryColourYRaw(int addr, int y1);
|
||||
bool setTemporaryColourXY(int a, double x, double y);
|
||||
bool setTemporaryColourTemperature(int a, int kelvin);
|
||||
bool setTemporaryPrimaryDimLevel(int a, int n, double level);
|
||||
bool setTemporaryRGBDimLevels(int a, int r, int g, int b);
|
||||
bool setTemporaryWAFDimLevels(int a, int w, int amber, int freecolour);
|
||||
bool setTemporaryRGBWAFControl(int a, int control);
|
||||
bool copyReportToTemporary(int a);
|
||||
|
||||
// Step commands
|
||||
bool stepXUp(int a);
|
||||
bool stepXDown(int a);
|
||||
bool stepYUp(int a);
|
||||
bool stepYDown(int a);
|
||||
bool stepTcCooler(int a);
|
||||
bool stepTcWarmer(int a);
|
||||
|
||||
bool setColourRGBRaw(int addr, int r, int g, int b);
|
||||
bool setColour(int a, double x, double y);
|
||||
std::optional<int> getColourRaw(int a, int type);
|
||||
std::vector<double> getColour(int a);
|
||||
bool setColourRGB(int addr, int r, int g, int b);
|
||||
std::vector<int> getColourRGB(int a);
|
||||
|
||||
bool activateTemporaryColour(int a);
|
||||
|
||||
// Active-type queries
|
||||
std::optional<int> getPrimaryDimLevel(int a, int n);
|
||||
std::optional<int> getRedDimLevel(int a);
|
||||
std::optional<int> getGreenDimLevel(int a);
|
||||
std::optional<int> getBlueDimLevel(int a);
|
||||
std::optional<int> getWhiteDimLevel(int a);
|
||||
std::optional<int> getAmberDimLevel(int a);
|
||||
std::optional<int> getFreecolourDimLevel(int a);
|
||||
std::optional<int> getRGBWAFControl(int a);
|
||||
|
||||
// Temporary colour queries
|
||||
std::optional<int> getTemporaryXRaw(int a);
|
||||
std::optional<int> getTemporaryYRaw(int a);
|
||||
std::optional<int> getTemporaryColourTemperatureRaw(int a);
|
||||
std::optional<int> getTemporaryPrimaryDimLevel(int a, int n);
|
||||
std::optional<int> getTemporaryRedDimLevel(int a);
|
||||
std::optional<int> getTemporaryGreenDimLevel(int a);
|
||||
std::optional<int> getTemporaryBlueDimLevel(int a);
|
||||
std::optional<int> getTemporaryWhiteDimLevel(int a);
|
||||
std::optional<int> getTemporaryAmberDimLevel(int a);
|
||||
std::optional<int> getTemporaryFreecolourDimLevel(int a);
|
||||
std::optional<int> getTemporaryRGBWAFControl(int a);
|
||||
std::optional<int> getTemporaryColourType(int a);
|
||||
std::vector<double> getTemporaryColour(int a);
|
||||
std::optional<int> getTemporaryColorTemperature(int a);
|
||||
|
||||
// Report colour queries
|
||||
std::optional<int> getReportXRaw(int a);
|
||||
std::optional<int> getReportYRaw(int a);
|
||||
std::optional<int> getReportColourTemperatureRaw(int a);
|
||||
std::optional<int> getReportPrimaryDimLevel(int a, int n);
|
||||
std::optional<int> getReportRedDimLevel(int a);
|
||||
std::optional<int> getReportGreenDimLevel(int a);
|
||||
std::optional<int> getReportBlueDimLevel(int a);
|
||||
std::optional<int> getReportWhiteDimLevel(int a);
|
||||
std::optional<int> getReportAmberDimLevel(int a);
|
||||
std::optional<int> getReportFreecolourDimLevel(int a);
|
||||
std::optional<int> getReportRGBWAFControl(int a);
|
||||
std::optional<int> getReportColourType(int a);
|
||||
std::vector<double> getReportColour(int a);
|
||||
std::optional<int> getReportColorTemperature(int a);
|
||||
|
||||
std::optional<int> getNumberOfPrimaries(int a);
|
||||
std::optional<int> getPrimaryXRaw(int a, int n);
|
||||
std::optional<int> getPrimaryYRaw(int a, int n);
|
||||
std::optional<int> getPrimaryTy(int a, int n);
|
||||
|
||||
std::vector<double> getSceneColor(int a, int sense);
|
||||
|
||||
// Store / config
|
||||
bool storePrimaryTy(int a, int n, int ty);
|
||||
bool storePrimaryXY(int a, int n, double x, double y);
|
||||
bool storeColourTempLimitRaw(int a, int limitType, int mirek);
|
||||
bool storeColourTempLimit(int a, int limitType, int kelvin);
|
||||
bool setGearAutoActivate(int a, bool enable);
|
||||
bool assignColourToLinkedChannels(int a, int colourId);
|
||||
bool startAutoCalibration(int a);
|
||||
|
||||
// Direct queries
|
||||
std::optional<int> getGearFeaturesStatus(int a);
|
||||
std::optional<int> getRGBWAFControlDirect(int a);
|
||||
std::optional<int> getAssignedColourForChannel(int a, int channelId);
|
||||
std::optional<int> getExtendedVersion(int a);
|
||||
|
||||
private:
|
||||
DaliBase& base_;
|
||||
};
|
||||
111
include/errors.hpp
Normal file
111
include/errors.hpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
class DaliQueryException : public std::exception {
|
||||
public:
|
||||
enum class Code {
|
||||
busUnavailable,
|
||||
gatewayTimeout,
|
||||
deviceNoResponse,
|
||||
invalidFrame,
|
||||
invalidGatewayFrame,
|
||||
unknown,
|
||||
};
|
||||
|
||||
DaliQueryException(Code code,
|
||||
std::string message,
|
||||
std::optional<int> addr = std::nullopt,
|
||||
std::optional<int> cmd = std::nullopt)
|
||||
: code_(code), message_(std::move(message)), addr_(addr), cmd_(cmd) {}
|
||||
|
||||
const char* what() const noexcept override { return message_.c_str(); }
|
||||
Code code() const { return code_; }
|
||||
const std::optional<int>& addr() const { return addr_; }
|
||||
const std::optional<int>& cmd() const { return cmd_; }
|
||||
|
||||
private:
|
||||
Code code_ = Code::unknown;
|
||||
std::string message_;
|
||||
std::optional<int> addr_;
|
||||
std::optional<int> cmd_;
|
||||
};
|
||||
|
||||
class DaliBusUnavailableException : public DaliQueryException {
|
||||
public:
|
||||
explicit DaliBusUnavailableException(std::optional<int> addr = std::nullopt,
|
||||
std::optional<int> cmd = std::nullopt)
|
||||
: DaliQueryException(Code::busUnavailable, "Bus unavailable", addr, cmd) {}
|
||||
};
|
||||
|
||||
class DaliGatewayTimeoutException : public DaliQueryException {
|
||||
public:
|
||||
explicit DaliGatewayTimeoutException(std::optional<int> addr = std::nullopt,
|
||||
std::optional<int> cmd = std::nullopt)
|
||||
: DaliQueryException(Code::gatewayTimeout, "Gateway no response", addr, cmd) {}
|
||||
};
|
||||
|
||||
class DaliDeviceNoResponseException : public DaliQueryException {
|
||||
public:
|
||||
explicit DaliDeviceNoResponseException(std::optional<int> addr = std::nullopt,
|
||||
std::optional<int> cmd = std::nullopt)
|
||||
: DaliQueryException(Code::deviceNoResponse, "Device no response", addr, cmd) {}
|
||||
};
|
||||
|
||||
class DaliInvalidFrameException : public DaliQueryException {
|
||||
public:
|
||||
explicit DaliInvalidFrameException(std::optional<int> addr = std::nullopt,
|
||||
std::optional<int> cmd = std::nullopt)
|
||||
: DaliQueryException(Code::invalidFrame, "Invalid frame", addr, cmd) {}
|
||||
};
|
||||
|
||||
class DaliInvalidGatewayFrameException : public DaliQueryException {
|
||||
public:
|
||||
explicit DaliInvalidGatewayFrameException(std::optional<int> addr = std::nullopt,
|
||||
std::optional<int> cmd = std::nullopt)
|
||||
: DaliQueryException(Code::invalidGatewayFrame, "Invalid gateway frame", addr, cmd) {}
|
||||
};
|
||||
|
||||
inline std::string mapDaliErrorToMessage(const DaliQueryException& e) {
|
||||
switch (e.code()) {
|
||||
case DaliQueryException::Code::busUnavailable:
|
||||
return "dali.error.bus_unavailable";
|
||||
case DaliQueryException::Code::gatewayTimeout:
|
||||
return "dali.error.gateway_timeout";
|
||||
case DaliQueryException::Code::deviceNoResponse:
|
||||
return "dali.error.device_no_response";
|
||||
case DaliQueryException::Code::invalidFrame:
|
||||
return "dali.error.invalid_frame";
|
||||
case DaliQueryException::Code::invalidGatewayFrame:
|
||||
return "dali.error.invalid_gateway_frame";
|
||||
case DaliQueryException::Code::unknown:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "dali.error.unknown";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<T> daliSafe(const std::function<T()>& action,
|
||||
const std::function<void(const std::string&)>& onError = nullptr,
|
||||
bool rethrowOthers = false) {
|
||||
#if defined(__cpp_exceptions)
|
||||
try {
|
||||
return action();
|
||||
} catch (const DaliQueryException& e) {
|
||||
if (onError) onError(mapDaliErrorToMessage(e));
|
||||
return std::nullopt;
|
||||
} catch (...) {
|
||||
if (rethrowOthers) throw;
|
||||
if (onError) onError("Unexpected error");
|
||||
return std::nullopt;
|
||||
}
|
||||
#else
|
||||
(void)onError;
|
||||
(void)rethrowOthers;
|
||||
return action();
|
||||
#endif
|
||||
}
|
||||
59
include/log.hpp
Normal file
59
include/log.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
enum class LogLevel { debug = 0, info = 1, warning = 2, error = 3 };
|
||||
|
||||
class DaliLog {
|
||||
public:
|
||||
static DaliLog& instance() {
|
||||
static DaliLog inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
void setLevel(LogLevel level) {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
level_ = level;
|
||||
}
|
||||
|
||||
LogLevel currentLevel() const { return level_; }
|
||||
|
||||
void setSink(std::function<void(const std::string&)> sink) {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
sink_ = std::move(sink);
|
||||
}
|
||||
|
||||
void debugLog(const std::string& message) { log(LogLevel::debug, "DEBUG", message); }
|
||||
void infoLog(const std::string& message) { log(LogLevel::info, "INFO", message); }
|
||||
void warningLog(const std::string& message) { log(LogLevel::warning, "WARNING", message); }
|
||||
void errorLog(const std::string& message) { log(LogLevel::error, "ERROR", message); }
|
||||
|
||||
std::vector<std::string> logMessages() const {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
return logs_;
|
||||
}
|
||||
|
||||
void clearLogs() {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
logs_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
DaliLog() = default;
|
||||
|
||||
void log(LogLevel level, const char* tag, const std::string& message) {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
if (static_cast<int>(level) < static_cast<int>(level_)) return;
|
||||
const std::string line = std::string("[") + tag + "] " + message;
|
||||
logs_.push_back(line);
|
||||
if (sink_) sink_(line);
|
||||
}
|
||||
|
||||
mutable std::mutex mu_;
|
||||
LogLevel level_ = LogLevel::info;
|
||||
std::vector<std::string> logs_;
|
||||
std::function<void(const std::string&)> sink_;
|
||||
};
|
||||
144
include/model_value.hpp
Normal file
144
include/model_value.hpp
Normal file
@@ -0,0 +1,144 @@
|
||||
#pragma once
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
class DaliValue {
|
||||
public:
|
||||
using Array = std::vector<DaliValue>;
|
||||
using Object = std::map<std::string, DaliValue>;
|
||||
|
||||
using Variant = std::variant<std::monostate, bool, int64_t, double, std::string, Array, Object>;
|
||||
|
||||
DaliValue() = default;
|
||||
DaliValue(std::nullptr_t) : value_(std::monostate{}) {}
|
||||
DaliValue(bool v) : value_(v) {}
|
||||
DaliValue(int v) : value_(static_cast<int64_t>(v)) {}
|
||||
DaliValue(int64_t v) : value_(v) {}
|
||||
DaliValue(double v) : value_(v) {}
|
||||
DaliValue(const char* v) : value_(std::string(v)) {}
|
||||
DaliValue(std::string v) : value_(std::move(v)) {}
|
||||
DaliValue(Array v) : value_(std::move(v)) {}
|
||||
DaliValue(Object v) : value_(std::move(v)) {}
|
||||
|
||||
bool isNull() const { return std::holds_alternative<std::monostate>(value_); }
|
||||
bool isBool() const { return std::holds_alternative<bool>(value_); }
|
||||
bool isInt() const { return std::holds_alternative<int64_t>(value_); }
|
||||
bool isDouble() const { return std::holds_alternative<double>(value_); }
|
||||
bool isString() const { return std::holds_alternative<std::string>(value_); }
|
||||
bool isArray() const { return std::holds_alternative<Array>(value_); }
|
||||
bool isObject() const { return std::holds_alternative<Object>(value_); }
|
||||
|
||||
std::optional<bool> asBool() const {
|
||||
if (isBool()) return std::get<bool>(value_);
|
||||
if (isInt()) return std::get<int64_t>(value_) != 0;
|
||||
if (isString()) {
|
||||
const auto& s = std::get<std::string>(value_);
|
||||
if (s == "true" || s == "TRUE" || s == "1") return true;
|
||||
if (s == "false" || s == "FALSE" || s == "0") return false;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<int> asInt() const {
|
||||
if (isInt()) return static_cast<int>(std::get<int64_t>(value_));
|
||||
if (isDouble()) return static_cast<int>(std::get<double>(value_));
|
||||
if (isString()) {
|
||||
const auto& s = std::get<std::string>(value_);
|
||||
if (s.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
const long parsed = std::strtol(s.c_str(), &end, 10);
|
||||
if (errno != 0 || end == s.c_str() || *end != '\0') {
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<int>(parsed);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<double> asDouble() const {
|
||||
if (isDouble()) return std::get<double>(value_);
|
||||
if (isInt()) return static_cast<double>(std::get<int64_t>(value_));
|
||||
if (isString()) {
|
||||
const auto& s = std::get<std::string>(value_);
|
||||
if (s.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
const double parsed = std::strtod(s.c_str(), &end);
|
||||
if (errno != 0 || end == s.c_str() || *end != '\0') {
|
||||
return std::nullopt;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> asString() const {
|
||||
if (isString()) return std::get<std::string>(value_);
|
||||
if (isBool()) return std::get<bool>(value_) ? "true" : "false";
|
||||
if (isInt()) return std::to_string(std::get<int64_t>(value_));
|
||||
if (isDouble()) return std::to_string(std::get<double>(value_));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const Array* asArray() const {
|
||||
if (!isArray()) return nullptr;
|
||||
return &std::get<Array>(value_);
|
||||
}
|
||||
|
||||
const Object* asObject() const {
|
||||
if (!isObject()) return nullptr;
|
||||
return &std::get<Object>(value_);
|
||||
}
|
||||
|
||||
Array* asArray() {
|
||||
if (!isArray()) return nullptr;
|
||||
return &std::get<Array>(value_);
|
||||
}
|
||||
|
||||
Object* asObject() {
|
||||
if (!isObject()) return nullptr;
|
||||
return &std::get<Object>(value_);
|
||||
}
|
||||
|
||||
const Variant& variant() const { return value_; }
|
||||
Variant& variant() { return value_; }
|
||||
|
||||
private:
|
||||
Variant value_;
|
||||
};
|
||||
|
||||
inline const DaliValue* getObjectValue(const DaliValue::Object& obj, const std::string& key) {
|
||||
const auto it = obj.find(key);
|
||||
if (it == obj.end()) return nullptr;
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
inline std::optional<int> getObjectInt(const DaliValue::Object& obj, const std::string& key) {
|
||||
const auto* v = getObjectValue(obj, key);
|
||||
if (!v) return std::nullopt;
|
||||
return v->asInt();
|
||||
}
|
||||
|
||||
inline std::optional<bool> getObjectBool(const DaliValue::Object& obj, const std::string& key) {
|
||||
const auto* v = getObjectValue(obj, key);
|
||||
if (!v) return std::nullopt;
|
||||
return v->asBool();
|
||||
}
|
||||
|
||||
inline std::optional<std::string> getObjectString(const DaliValue::Object& obj,
|
||||
const std::string& key) {
|
||||
const auto* v = getObjectValue(obj, key);
|
||||
if (!v) return std::nullopt;
|
||||
return v->asString();
|
||||
}
|
||||
22
include/query_scheduler.hpp
Normal file
22
include/query_scheduler.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
class DaliQueryScheduler {
|
||||
public:
|
||||
static DaliQueryScheduler& instance() {
|
||||
static DaliQueryScheduler inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
auto run(Fn&& action) -> decltype(action()) {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
return action();
|
||||
}
|
||||
|
||||
private:
|
||||
DaliQueryScheduler() = default;
|
||||
std::mutex mu_;
|
||||
};
|
||||
62
include/sequence.hpp
Normal file
62
include/sequence.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include "model_value.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Mirrors lib/dali/sequence.dart command type names.
|
||||
enum class DaliCommandType {
|
||||
setBright,
|
||||
on,
|
||||
off,
|
||||
toScene,
|
||||
setScene,
|
||||
removeScene,
|
||||
addToGroup,
|
||||
removeFromGroup,
|
||||
setFadeTime,
|
||||
setFadeRate,
|
||||
wait,
|
||||
modifyShortAddress,
|
||||
deleteShortAddress,
|
||||
};
|
||||
|
||||
std::string toString(DaliCommandType type);
|
||||
DaliCommandType commandTypeFromString(const std::string& name,
|
||||
DaliCommandType fallback = DaliCommandType::setBright);
|
||||
|
||||
struct DaliCommandParams {
|
||||
DaliValue::Object data;
|
||||
|
||||
DaliCommandParams() = default;
|
||||
explicit DaliCommandParams(DaliValue::Object d) : data(std::move(d)) {}
|
||||
|
||||
int getInt(const std::string& key, int def = 0) const;
|
||||
DaliCommandParams copy() const;
|
||||
|
||||
DaliValue::Object toJson() const;
|
||||
static DaliCommandParams fromJson(const DaliValue::Object& json);
|
||||
};
|
||||
|
||||
struct SequenceStep {
|
||||
std::string id;
|
||||
std::optional<std::string> remark;
|
||||
DaliCommandType type = DaliCommandType::setBright;
|
||||
DaliCommandParams params;
|
||||
|
||||
SequenceStep copy() const;
|
||||
|
||||
DaliValue::Object toJson() const;
|
||||
static SequenceStep fromJson(const DaliValue::Object& json);
|
||||
};
|
||||
|
||||
struct CommandSequence {
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::vector<SequenceStep> steps;
|
||||
|
||||
DaliValue::Object toJson() const;
|
||||
static CommandSequence fromJson(const DaliValue::Object& json);
|
||||
};
|
||||
37
include/sequence_store.hpp
Normal file
37
include/sequence_store.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "model_value.hpp"
|
||||
#include "sequence.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
constexpr const char* kSequencesKey = "command_sequences_v1";
|
||||
|
||||
class SequenceRepository {
|
||||
public:
|
||||
using LoadCallback = std::function<bool(const std::string& key, DaliValue* out)>;
|
||||
using SaveCallback = std::function<bool(const std::string& key, const DaliValue& value)>;
|
||||
|
||||
SequenceRepository() = default;
|
||||
SequenceRepository(LoadCallback loadCb, SaveCallback saveCb)
|
||||
: loadCallback_(std::move(loadCb)), saveCallback_(std::move(saveCb)) {}
|
||||
|
||||
void setLoadCallback(LoadCallback cb) { loadCallback_ = std::move(cb); }
|
||||
void setSaveCallback(SaveCallback cb) { saveCallback_ = std::move(cb); }
|
||||
|
||||
bool load();
|
||||
bool save() const;
|
||||
|
||||
const std::vector<CommandSequence>& sequences() const { return sequences_; }
|
||||
|
||||
void add(const CommandSequence& s);
|
||||
void remove(const std::string& id);
|
||||
void replace(const CommandSequence& s);
|
||||
|
||||
private:
|
||||
std::vector<CommandSequence> sequences_;
|
||||
LoadCallback loadCallback_;
|
||||
SaveCallback saveCallback_;
|
||||
};
|
||||
Reference in New Issue
Block a user