GroupState.h 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. #include <stddef.h>
  2. #include <inttypes.h>
  3. #include <MiLightConstants.h>
  4. #include <MiLightRadioConfig.h>
  5. #include <GroupStateField.h>
  6. #include <ArduinoJson.h>
  7. #ifndef _GROUP_STATE_H
  8. #define _GROUP_STATE_H
  9. // enable to add debugging on state
  10. // #define DEBUG_STATE
  11. struct BulbId {
  12. uint16_t deviceId;
  13. uint8_t groupId;
  14. MiLightRemoteType deviceType;
  15. BulbId();
  16. BulbId(const BulbId& other);
  17. BulbId(const uint16_t deviceId, const uint8_t groupId, const MiLightRemoteType deviceType);
  18. bool operator==(const BulbId& other);
  19. void operator=(const BulbId& other);
  20. };
  21. enum BulbMode {
  22. BULB_MODE_WHITE,
  23. BULB_MODE_COLOR,
  24. BULB_MODE_SCENE,
  25. BULB_MODE_NIGHT
  26. };
  27. enum class IncrementDirection : unsigned {
  28. INCREASE = 1,
  29. DECREASE = -1U
  30. };
  31. static const char* BULB_MODE_NAMES[] = {
  32. "white",
  33. "color",
  34. "scene",
  35. "night"
  36. };
  37. class GroupState {
  38. public:
  39. GroupState();
  40. GroupState(const GroupState& other);
  41. GroupState& operator=(const GroupState& other);
  42. // Convenience constructor that patches defaults with JSON state
  43. GroupState(const JsonObject& jsonState);
  44. void initFields();
  45. bool operator==(const GroupState& other) const;
  46. bool isEqualIgnoreDirty(const GroupState& other) const;
  47. void print(Stream& stream) const;
  48. bool isSetField(GroupStateField field) const;
  49. uint16_t getFieldValue(GroupStateField field) const;
  50. void setFieldValue(GroupStateField field, uint16_t value);
  51. bool clearField(GroupStateField field);
  52. bool isSetScratchField(GroupStateField field) const;
  53. uint16_t getScratchFieldValue(GroupStateField field) const;
  54. void setScratchFieldValue(GroupStateField field, uint16_t value);
  55. // 1 bit
  56. bool isSetState() const;
  57. MiLightStatus getState() const;
  58. bool setState(const MiLightStatus on);
  59. // Return true if status is ON or if the field is unset (i.e., defaults to ON)
  60. bool isOn() const;
  61. // 7 bits
  62. bool isSetBrightness() const;
  63. uint8_t getBrightness() const;
  64. bool setBrightness(uint8_t brightness);
  65. bool clearBrightness();
  66. // 8 bits
  67. bool isSetHue() const;
  68. uint16_t getHue() const;
  69. bool setHue(uint16_t hue);
  70. // 7 bits
  71. bool isSetSaturation() const;
  72. uint8_t getSaturation() const;
  73. bool setSaturation(uint8_t saturation);
  74. // 5 bits
  75. bool isSetMode() const;
  76. bool isSetEffect() const;
  77. uint8_t getMode() const;
  78. bool setMode(uint8_t mode);
  79. // 7 bits
  80. bool isSetKelvin() const;
  81. uint8_t getKelvin() const;
  82. uint16_t getMireds() const;
  83. bool setKelvin(uint8_t kelvin);
  84. bool setMireds(uint16_t mireds);
  85. // 3 bits
  86. bool isSetBulbMode() const;
  87. BulbMode getBulbMode() const;
  88. bool setBulbMode(BulbMode mode);
  89. // 1 bit
  90. bool isSetNightMode() const;
  91. bool isNightMode() const;
  92. bool setNightMode(bool nightMode);
  93. bool isDirty() const;
  94. inline bool setDirty();
  95. bool clearDirty();
  96. bool isMqttDirty() const;
  97. inline bool setMqttDirty();
  98. bool clearMqttDirty();
  99. // Clears all of the fields in THIS GroupState that have different values
  100. // than the provided group state.
  101. bool clearNonMatchingFields(const GroupState& other);
  102. // Patches this state with ONLY the set fields in the other. Returns
  103. // true if there were any changes.
  104. bool patch(const GroupState& other);
  105. // Patches this state with the fields defined in the JSON state. Returns
  106. // true if there were any changes.
  107. bool patch(const JsonObject& state);
  108. // It's a little weird to need to pass in a BulbId here. The purpose is to
  109. // support fields like DEVICE_ID, which aren't otherweise available to the
  110. // state in this class. The alternative is to have every GroupState object
  111. // keep a reference to its BulbId, which feels too heavy-weight.
  112. void applyField(JsonObject& state, const BulbId& bulbId, GroupStateField field) const;
  113. void applyState(JsonObject& state, const BulbId& bulbId, GroupStateField* fields, size_t numFields) const;
  114. // Attempt to keep track of increment commands in such a way that we can
  115. // know what state it's in. When we get an increment command (like "increase
  116. // brightness"):
  117. // 1. If there is no value in the scratch state: assume real state is in
  118. // the furthest value from the direction of the command. For example,
  119. // if we get "increase," assume the value was 0.
  120. // 2. If there is a value in the scratch state, apply the command to it.
  121. // For example, if we get "decrease," subtract 1 from the scratch.
  122. // 3. When scratch reaches a known extreme (either min or max), set the
  123. // persistent field to that value
  124. // 4. If there is already a known value for the state, apply it rather
  125. // than messing with scratch state.
  126. //
  127. // returns true if a (real, not scratch) state change was made
  128. bool applyIncrementCommand(GroupStateField field, IncrementDirection dir);
  129. void load(Stream& stream);
  130. void dump(Stream& stream) const;
  131. void debugState(char const *debugMessage) const;
  132. static const GroupState& defaultState(MiLightRemoteType remoteType);
  133. private:
  134. static const size_t DATA_LONGS = 2;
  135. union StateData {
  136. uint32_t rawData[DATA_LONGS];
  137. struct Fields {
  138. uint32_t
  139. _state : 1,
  140. _brightness : 7,
  141. _hue : 8,
  142. _saturation : 7,
  143. _mode : 4,
  144. _bulbMode : 3,
  145. _isSetState : 1,
  146. _isSetHue : 1;
  147. uint32_t
  148. _kelvin : 7,
  149. _isSetBrightness : 1,
  150. _isSetSaturation : 1,
  151. _isSetMode : 1,
  152. _isSetKelvin : 1,
  153. _isSetBulbMode : 1,
  154. _brightnessColor : 7,
  155. _brightnessMode : 7,
  156. _isSetBrightnessColor : 1,
  157. _isSetBrightnessMode : 1,
  158. _dirty : 1,
  159. _mqttDirty : 1,
  160. _isSetNightMode : 1,
  161. _isNightMode : 1;
  162. } fields;
  163. };
  164. // Transient scratchpad that is never persisted. Used to track and compute state for
  165. // protocols that only have increment commands (like CCT).
  166. union TransientData {
  167. uint16_t rawData;
  168. struct Fields {
  169. uint16_t
  170. _isSetKelvinScratch : 1,
  171. _kelvinScratch : 7,
  172. _isSetBrightnessScratch : 1,
  173. _brightnessScratch : 8;
  174. } fields;
  175. };
  176. StateData state;
  177. TransientData scratchpad;
  178. void applyColor(JsonObject& state, uint8_t r, uint8_t g, uint8_t b) const;
  179. void applyColor(JsonObject& state) const;
  180. // Apply OpenHAB-style color, e.g., {"color":"0,0,0"}
  181. void applyOhColor(JsonObject& state) const;
  182. };
  183. extern const BulbId DEFAULT_BULB_ID;
  184. #endif