GroupState.h 6.5 KB

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