solax-py-library 1.0.0.24__py3-none-any.whl → 1.0.0.2501__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. solax_py_library/__init__.py +1 -1
  2. solax_py_library/device/constant/inverter_model_info.py +312 -312
  3. solax_py_library/device/core/interver/__init__.py +36 -36
  4. solax_py_library/device/core/interver/base.py +215 -215
  5. solax_py_library/device/types/inverter_config.py +41 -41
  6. solax_py_library/device/types/modbus_point.py +30 -30
  7. solax_py_library/exception.py +10 -10
  8. solax_py_library/smart_scene/__init__.py +0 -0
  9. solax_py_library/smart_scene/constant/__init__.py +0 -0
  10. solax_py_library/smart_scene/constant/message_entry.py +338 -0
  11. solax_py_library/smart_scene/core/__init__.py +0 -0
  12. solax_py_library/smart_scene/core/action/__init__.py +0 -0
  13. solax_py_library/smart_scene/core/condition/__init__.py +0 -0
  14. solax_py_library/smart_scene/core/condition/base.py +12 -0
  15. solax_py_library/smart_scene/core/condition/cabinet_condition.py +104 -0
  16. solax_py_library/smart_scene/core/condition/date_condition.py +26 -0
  17. solax_py_library/smart_scene/core/condition/price_condition.py +105 -0
  18. solax_py_library/smart_scene/core/condition/system_condition.py +41 -0
  19. solax_py_library/smart_scene/core/condition/weather_condition.py +67 -0
  20. solax_py_library/smart_scene/exceptions/__init__.py +0 -0
  21. solax_py_library/smart_scene/exceptions/price.py +5 -0
  22. solax_py_library/smart_scene/exceptions/smart_scene.py +81 -0
  23. solax_py_library/smart_scene/exceptions/weather.py +5 -0
  24. solax_py_library/smart_scene/types/__init__.py +0 -0
  25. solax_py_library/smart_scene/types/action.py +156 -0
  26. solax_py_library/smart_scene/types/condition.py +293 -0
  27. solax_py_library/smart_scene/types/smart_scene_content.py +173 -0
  28. solax_py_library/snap_shot/__init__.py +3 -3
  29. solax_py_library/snap_shot/constant/__init__.py +5 -5
  30. solax_py_library/snap_shot/constant/crc_table.py +258 -258
  31. solax_py_library/snap_shot/core/__init__.py +9 -9
  32. solax_py_library/snap_shot/core/base_modbus.py +14 -14
  33. solax_py_library/snap_shot/exceptions/__init__.py +3 -3
  34. solax_py_library/snap_shot/exceptions/snap_shot.py +9 -9
  35. solax_py_library/snap_shot/types/__init__.py +15 -15
  36. solax_py_library/snap_shot/types/address.py +39 -39
  37. solax_py_library/upload/__init__.py +3 -3
  38. solax_py_library/upload/api/__init__.py +3 -3
  39. solax_py_library/upload/api/service.py +24 -24
  40. solax_py_library/upload/core/__init__.py +3 -3
  41. solax_py_library/upload/core/data_adapter/__init__.py +5 -5
  42. solax_py_library/upload/core/data_adapter/base.py +9 -9
  43. solax_py_library/upload/core/data_adapter/csv.py +26 -26
  44. solax_py_library/upload/core/upload_service/__init__.py +15 -15
  45. solax_py_library/upload/core/upload_service/base.py +43 -43
  46. solax_py_library/upload/exceptions/__init__.py +8 -8
  47. solax_py_library/upload/exceptions/upload_error.py +21 -21
  48. solax_py_library/upload/test/test_ftp.py +113 -113
  49. solax_py_library/upload/types/__init__.py +11 -11
  50. solax_py_library/upload/types/client.py +19 -19
  51. solax_py_library/upload/types/ftp.py +37 -37
  52. solax_py_library/utils/cloud_client.py +213 -0
  53. solax_py_library/utils/common.py +38 -38
  54. solax_py_library/utils/struct_util.py +30 -30
  55. solax_py_library/utils/time_util.py +5 -0
  56. {solax_py_library-1.0.0.24.dist-info → solax_py_library-1.0.0.2501.dist-info}/METADATA +1 -1
  57. solax_py_library-1.0.0.2501.dist-info/RECORD +68 -0
  58. solax_py_library-1.0.0.24.dist-info/RECORD +0 -46
  59. {solax_py_library-1.0.0.24.dist-info → solax_py_library-1.0.0.2501.dist-info}/WHEEL +0 -0
@@ -0,0 +1,67 @@
1
+
2
+ class WeatherCondition(BaseCondition):
3
+ def __init__(self, smart_scene_service):
4
+ super().__init__(smart_scene_service)
5
+ self.type = IF_ELE_TYPE
6
+ self.buy = True
7
+ self.child_type_list = ["irradiance", "temperature"]
8
+ self.value = {}
9
+ self.unit = ""
10
+ self.meet_func = self.meet_weather_condition
11
+
12
+ def update_value(
13
+ self,
14
+ ):
15
+ self.value = self.smart_scene_service.get_weather_data_from_redis()
16
+
17
+ def meet_weather_condition(self, data: WeatherConditionItemData, ctx):
18
+ if not self.value:
19
+ return False
20
+ child_data = data.childData
21
+ child_type = data.childType
22
+ function_value = child_data.function
23
+ data_value = child_data.data
24
+ nearest_time, right_time = self.smart_scene_service.get_rounded_times()
25
+ if nearest_time not in self.value["timeList"]:
26
+ time_now = right_time
27
+ else:
28
+ time_now = nearest_time
29
+ index = self.value["timeList"].index(time_now)
30
+ app_log.info(
31
+ f"最近时间点为: {nearest_time}, 右侧时间点为: {right_time}, 数组索引为: {index}"
32
+ )
33
+ if child_type == WeatherConditionType.irradiance:
34
+ return self.meet_func_irradiance(function_value, data_value, index)
35
+ elif child_type == WeatherConditionType.temperature:
36
+ app_log.info(f"当前实际值为: {self.value[child_type]['valueList'][index]}")
37
+ return self.smart_scene_service.compare_the_magnitudes(
38
+ function_value,
39
+ self.value[child_type]["valueList"][index],
40
+ data_value[0],
41
+ )
42
+ return False
43
+
44
+ def meet_func_irradiance(self, function_value, data_value, index):
45
+ """太阳辐照度判断"""
46
+ irradiance = data_value[0]
47
+ duration = data_value[1]
48
+ meet_num = 0
49
+ meet_flag = False
50
+ if duration == 0:
51
+ meet_flag = True
52
+ elif duration > 24:
53
+ pass
54
+ else:
55
+ # 1. 保证累计duration个小时大于200,
56
+ for value in self.value["irradiance"]["valueList"]:
57
+ if value > 200:
58
+ meet_num += 1
59
+ if meet_num >= duration * 4:
60
+ meet_flag = True
61
+ break
62
+ if not meet_flag:
63
+ return False
64
+ # 2. 再判断当前太阳辐照度
65
+ return self.smart_scene_service.compare_the_magnitudes(
66
+ function_value, self.value["irradiance"]["valueList"][index], irradiance
67
+ )
File without changes
@@ -0,0 +1,5 @@
1
+ from solax_py_library.exception import SolaxBaseError
2
+
3
+
4
+ class ElectricityPriceFailure(SolaxBaseError):
5
+ message = "cloud__weather_failure"
@@ -0,0 +1,81 @@
1
+ from solax_py_library.exception import SolaxBaseError
2
+
3
+
4
+ class OnlyPositive(SolaxBaseError):
5
+ message = "smart_scene__time_range"
6
+
7
+
8
+ class TimeRange(SolaxBaseError):
9
+ message = "smart_scene__irradiance_only_positive"
10
+
11
+
12
+ class IrradianceOnlyPositive(SolaxBaseError):
13
+ message = "smart_scene__export_limit_num"
14
+
15
+
16
+ class ExportLimitNum(SolaxBaseError):
17
+ message = "smart_scene__export_limit_percent"
18
+
19
+
20
+ class ExportLimitPercent(SolaxBaseError):
21
+ message = "smart_scene__import_limit_num"
22
+
23
+
24
+ class ImportLimitNum(SolaxBaseError):
25
+ message = "smart_scene__import_only_positive"
26
+
27
+
28
+ class ImportOnlyPositive(SolaxBaseError):
29
+ message = "smart_scene__soc_limit"
30
+
31
+
32
+ class SocLimit(SolaxBaseError):
33
+ message = "smart_scene__active_power_limit_num"
34
+
35
+
36
+ class ActivePowerLimitNum(SolaxBaseError):
37
+ message = "smart_scene__reactive_power_limit_num"
38
+
39
+
40
+ class ReactivePowerLimitNum(SolaxBaseError):
41
+ message = "smart_scene__energy_limit"
42
+
43
+
44
+ class EnergyLimit(SolaxBaseError):
45
+ message = "smart_scene__power_limit_num"
46
+
47
+
48
+ class PowerLimitNum(SolaxBaseError):
49
+ message = "smart_scene__battery_power_limit_num"
50
+
51
+
52
+ class BatteryPowerLimitNum(SolaxBaseError):
53
+ message = "smart_scene__pv_only_ge_0"
54
+
55
+
56
+ class PvOnlyGe0(SolaxBaseError):
57
+ message = "smart_scene__miss_param"
58
+
59
+
60
+ class MissParam(SolaxBaseError):
61
+ message = "smart_scene__count_limit"
62
+
63
+
64
+ class CountLimit(SolaxBaseError):
65
+ message = "smart_scene__name_length_limit"
66
+
67
+
68
+ class NameLengthLimit(SolaxBaseError):
69
+ message = "smart_scene__unique_limit"
70
+
71
+
72
+ class UniqueLimit(SolaxBaseError):
73
+ message = "cloud__electricity_price_failure"
74
+
75
+
76
+ class ElectricityPriceFailure(SolaxBaseError):
77
+ message = "cloud__weather_failure"
78
+
79
+
80
+ class WeatherFailure(SolaxBaseError):
81
+ message = "cloud__weather_failure"
@@ -0,0 +1,5 @@
1
+ from solax_py_library.exception import SolaxBaseError
2
+
3
+
4
+ class WeatherFailure(SolaxBaseError):
5
+ message = "cloud__weather_failure"
File without changes
@@ -0,0 +1,156 @@
1
+ from enum import Enum
2
+ from typing import List, Union
3
+
4
+ from pydantic import BaseModel
5
+
6
+ from domain.ems_enum.smart_scene.base import MESSAGE_ENTRY
7
+ from settings.const import DeviceInfo
8
+
9
+
10
+ class ActionType(str, Enum):
11
+ EMS1000 = "EMS1000"
12
+ system = "system"
13
+
14
+
15
+ class EmsActionType(str, Enum):
16
+ DoControl = "DoControl"
17
+
18
+
19
+ class SystemActionType(str, Enum):
20
+ systemSwitch = "systemSwitch"
21
+ exportControl = "exportControl"
22
+ importControl = "importControl"
23
+ workMode = "workMode"
24
+
25
+
26
+ class DoControl(BaseModel):
27
+ DoNumber: int
28
+ DoValue: int
29
+
30
+
31
+ class ActionChildData(BaseModel):
32
+ data: List[Union[int, float, DoControl]]
33
+
34
+
35
+ class SystemActionItemData(BaseModel):
36
+ childType: SystemActionType
37
+ childData: ActionChildData
38
+
39
+ def to_text(self, lang, cabinet_type):
40
+ if self.childType == SystemActionType.systemSwitch:
41
+ switch = "off" if self.childData.data[0] == 0 else "on"
42
+ return MESSAGE_ENTRY[self.childType][lang].format(
43
+ MESSAGE_ENTRY[switch][lang]
44
+ )
45
+ elif self.childType == SystemActionType.exportControl:
46
+ if self.childData.data[0] == 0:
47
+ return MESSAGE_ENTRY["exportControlOff"][lang]
48
+ else:
49
+ switch = "on"
50
+ mode = "total" if self.childData.data[1] == 1 else "per phase"
51
+ unit = "kW" if self.childData.data[3] == 2 else "%"
52
+ return MESSAGE_ENTRY[self.childType][lang].format(
53
+ MESSAGE_ENTRY[switch][lang],
54
+ MESSAGE_ENTRY[mode][lang],
55
+ self.childData.data[2],
56
+ unit,
57
+ )
58
+ elif self.childType == SystemActionType.importControl:
59
+ if self.childData.data[0] == 0:
60
+ return MESSAGE_ENTRY["importControlOff"][lang]
61
+ else:
62
+ if cabinet_type in DeviceInfo.TRENE_CABINET_ENUM:
63
+ msg = (
64
+ "importControl_standby"
65
+ if self.childData.data[1] == 0
66
+ else "importControl_discharge"
67
+ )
68
+ return MESSAGE_ENTRY[self.childType][lang].format(
69
+ MESSAGE_ENTRY["on"][lang],
70
+ MESSAGE_ENTRY[msg][lang],
71
+ self.childData.data[2],
72
+ )
73
+ else:
74
+ return MESSAGE_ENTRY["importControl_AELIO"][lang].format(
75
+ MESSAGE_ENTRY["on"][lang], self.childData.data[1]
76
+ )
77
+ elif self.childType == SystemActionType.workMode:
78
+ return self.work_mode_to_text(lang)
79
+
80
+ def work_mode_to_text(self, lang):
81
+ work_mode = {
82
+ 0: "Self-use",
83
+ 1: "Feedin priority",
84
+ 2: "Back up mode",
85
+ 3: "Manual mode",
86
+ 4: "Peak Shaving",
87
+ 16: "VPP",
88
+ }
89
+ # 3: 手动(3 强充,4 强放,5 停止充放电)
90
+ manual_mode = {
91
+ 3: "Forced charging",
92
+ 4: "Forced discharging",
93
+ 5: "Stop charging and discharging",
94
+ }
95
+ vpp_mode = {
96
+ 1: "Power Control Mode",
97
+ 2: "Electric Quantity Target Control Mode",
98
+ 3: "SOC Target Control Mode",
99
+ 4: "Push Power - Positive/Negative Mode",
100
+ 5: "Push Power - Zero Mode",
101
+ 6: "Self-Consume - Charge/Discharge Mode",
102
+ 7: "Self-Consume - Charge Only Mode",
103
+ 8: "PV&BAT Individual Setting – Duration Mode",
104
+ 9: "PV&BAT Individual Setting – Target SOC Mode",
105
+ }
106
+ value_data = self.childData.data
107
+ # 手动模式
108
+ if value_data[0] in [3]:
109
+ if value_data[1] in [3, 4]:
110
+ return MESSAGE_ENTRY[work_mode[value_data[0]]][lang].format(
111
+ MESSAGE_ENTRY[manual_mode[value_data[1]]][lang].format(
112
+ value_data[2], value_data[3]
113
+ )
114
+ )
115
+ else:
116
+ return MESSAGE_ENTRY[work_mode[value_data[0]]][lang].format(
117
+ MESSAGE_ENTRY[manual_mode[value_data[1]]][lang]
118
+ )
119
+ elif value_data[0] in [16]:
120
+ mode = vpp_mode[value_data[1]]
121
+ if value_data[1] in [1, 2, 3, 8]:
122
+ return MESSAGE_ENTRY[mode][lang].format(value_data[2], value_data[3])
123
+ elif value_data[1] in [4]:
124
+ return MESSAGE_ENTRY[mode][lang].format(value_data[2])
125
+ elif value_data[1] in [5, 6, 7]:
126
+ return MESSAGE_ENTRY[mode][lang]
127
+ elif value_data[1] in [9]:
128
+ return MESSAGE_ENTRY[mode][lang].format(
129
+ value_data[2], value_data[3], value_data[4]
130
+ )
131
+ else:
132
+ return MESSAGE_ENTRY[work_mode[value_data[0]]][lang]
133
+ return ""
134
+
135
+
136
+ class EmsActionItemData(BaseModel):
137
+ childType: EmsActionType
138
+ childData: ActionChildData
139
+
140
+ def to_text(self, lang, cabinet_type):
141
+ if self.childType == EmsActionType.DoControl:
142
+ message = ""
143
+ for do_info in self.childData.data:
144
+ message += MESSAGE_ENTRY[self.childType][lang].format(
145
+ do_info.DoNumber,
146
+ do_info.DoValue,
147
+ )
148
+ return message
149
+
150
+
151
+ class SmartSceneAction(BaseModel):
152
+ type: ActionType
153
+ data: List[Union[EmsActionItemData, SystemActionItemData]]
154
+
155
+ def to_text(self, lang, cabinet_type):
156
+ return [item.to_text(lang, cabinet_type) for item in self.data]
@@ -0,0 +1,293 @@
1
+ from enum import IntEnum, Enum
2
+ from typing import Optional, List, Union, Any
3
+
4
+ from pydantic import BaseModel, validator, root_validator
5
+
6
+ from solax_py_library.smart_scene.constant.message_entry import MESSAGE_ENTRY
7
+
8
+
9
+ class LogicFunc(IntEnum):
10
+ OR = 0
11
+ AND = 1
12
+
13
+
14
+ class ConditionFunc(IntEnum):
15
+ GT = 100
16
+ LT = 101
17
+ EQ = 102
18
+
19
+
20
+ class RepeatFunc(IntEnum):
21
+ ONCE = 103
22
+ EVERYDAY = 104
23
+ WEEKDAY = 105
24
+ WEEKEND = 106
25
+ CUSTOM = 107
26
+
27
+
28
+ class ConditionType(str, Enum):
29
+ date = "date"
30
+ weather = "weather"
31
+ buyingPrice = "buyingPrice"
32
+ sellingPrice = "sellingPrice"
33
+ systemCondition = "systemCondition"
34
+ cabinet = "cabinet"
35
+
36
+
37
+ class WeatherConditionType(str, Enum):
38
+ irradiance = "irradiance"
39
+ temperature = "temperature"
40
+
41
+
42
+ class PriceConditionType(str, Enum):
43
+ price = "price"
44
+ lowerPrice = "lowerPrice"
45
+ higherPrice = "higherPrice"
46
+ expensiveHours = "expensiveHours"
47
+ cheapestHours = "cheapestHours"
48
+
49
+
50
+ class DateConditionType(str, Enum):
51
+ time = "time"
52
+ duration = "duration"
53
+
54
+
55
+ class SystemConditionType(str, Enum):
56
+ systemSoc = "systemSoc"
57
+ systemImportPower = "systemImportPower" # 买电功率
58
+ systemExportPower = "systemExportPower" # 馈电功率
59
+
60
+
61
+ class CabinetConditionType(str, Enum):
62
+ cabinetAlarm = "cabinetAlarm"
63
+ cabinetSoc = "cabinetSoc"
64
+
65
+
66
+ class SmartSceneUnit(IntEnum):
67
+ PERCENT = 1
68
+ NUM = 2
69
+
70
+
71
+ class ConditionItemChildData(BaseModel):
72
+ function: Optional[ConditionFunc]
73
+ data: List[Any]
74
+
75
+
76
+ class PriceConditionItemData(BaseModel):
77
+ childType: PriceConditionType
78
+ childData: ConditionItemChildData
79
+
80
+ @validator("childData", always=True)
81
+ def _check_child_data(cls, value, values):
82
+ childType = values.get("childType")
83
+ if childType in {
84
+ PriceConditionType.lowerPrice,
85
+ PriceConditionType.higherPrice,
86
+ }:
87
+ assert value.data[0] > 0, ValueError
88
+ elif childType in {
89
+ PriceConditionType.expensiveHours,
90
+ PriceConditionType.cheapestHours,
91
+ }:
92
+ assert 1 <= value.data[2] <= 24, ValueError
93
+ return value
94
+
95
+ def to_text(self, lang, unit):
96
+ data = self.childData.data
97
+ func = self.childData.function
98
+ if self.childType == PriceConditionType.price:
99
+ return MESSAGE_ENTRY[self.childType][lang].format(
100
+ MESSAGE_ENTRY[str(func.value)][lang], data[0], unit
101
+ )
102
+ elif self.childType in {
103
+ PriceConditionType.lowerPrice,
104
+ PriceConditionType.higherPrice,
105
+ }:
106
+ return MESSAGE_ENTRY[self.childType][lang].format(
107
+ data[0], "%" if data[1] == 1 else unit
108
+ )
109
+ elif self.childType in {
110
+ PriceConditionType.expensiveHours,
111
+ PriceConditionType.cheapestHours,
112
+ }:
113
+ return MESSAGE_ENTRY[self.childType][lang].format(data[0], data[1], data[2])
114
+
115
+
116
+ class SystemConditionItemData(BaseModel):
117
+ childType: SystemConditionType
118
+ childData: ConditionItemChildData
119
+
120
+ @validator("childData", always=True)
121
+ def _check_child_data(cls, value, values):
122
+ childType = values.get("childType")
123
+ if childType in {
124
+ SystemConditionType.systemExportPower,
125
+ SystemConditionType.systemImportPower,
126
+ }:
127
+ assert 0 <= value.data[0] <= 100000, ValueError
128
+ value.data[0] = round(value.data[0], 2) # 功率保留两位小数
129
+ elif childType == SystemConditionType.systemSoc:
130
+ assert 5 <= value.data[0] <= 100, ValueError
131
+ return value
132
+
133
+ def to_text(self, lang, unit):
134
+ data = self.childData.data
135
+ func = self.childData.function
136
+ if self.childType == SystemConditionType.systemSoc:
137
+ return MESSAGE_ENTRY[self.childType][lang].format(
138
+ MESSAGE_ENTRY[str(func.value)][lang], data[0]
139
+ )
140
+ elif self.childType in {
141
+ SystemConditionType.systemImportPower,
142
+ SystemConditionType.systemExportPower,
143
+ }:
144
+ return MESSAGE_ENTRY[self.childType][lang].format(
145
+ MESSAGE_ENTRY[str(func.value)][lang], data[0]
146
+ )
147
+
148
+
149
+ class CabinetConditionItemData(BaseModel):
150
+ childType: CabinetConditionType
151
+ childData: ConditionItemChildData
152
+
153
+ @validator("childData", always=True)
154
+ def _check_child_data(cls, value, values):
155
+ childType = values.get("childType")
156
+ # if childType == CabinetConditionType.cabinetAlarm:
157
+ # assert value.data[0] in {
158
+ # AlarmLevel.TIPS,
159
+ # AlarmLevel.NORMAL,
160
+ # AlarmLevel.EMERGENCY,
161
+ # }, ValueError
162
+ if childType == CabinetConditionType.cabinetSoc:
163
+ assert 0 <= value.data[0] <= 100, ValueError
164
+ return value
165
+
166
+ def to_text(self, lang, unit):
167
+ data = self.childData.data
168
+ func = self.childData.function
169
+ if self.childType == CabinetConditionType.cabinetSoc:
170
+ return MESSAGE_ENTRY[self.childType][lang].format(
171
+ MESSAGE_ENTRY[str(func.value)][lang], data[0]
172
+ )
173
+ elif self.childType == CabinetConditionType.cabinetAlarm:
174
+ return MESSAGE_ENTRY[self.childType][lang].format(
175
+ MESSAGE_ENTRY[str(data[0])][lang], lang
176
+ )
177
+
178
+
179
+ class DateConditionItemData(BaseModel):
180
+ childType: DateConditionType
181
+ childData: ConditionItemChildData
182
+
183
+ @validator("childData", always=True)
184
+ def check_param(cls, value, values):
185
+ childType = values.get("childType")
186
+ data = value.data
187
+ if childType == DateConditionType.time:
188
+ assert isinstance(data[0], str), ValueError
189
+ elif childType == DateConditionType.duration:
190
+ assert isinstance(data[0], int), ValueError
191
+ return value
192
+
193
+ def to_text(self, lang, unit):
194
+ if self.childType == DateConditionType.duration:
195
+ return MESSAGE_ENTRY[self.childType][lang].format(self.childData.data[0])
196
+ elif self.childType == DateConditionType.time:
197
+ return MESSAGE_ENTRY[self.childType][lang] + "/" + self.childData.data[0]
198
+
199
+
200
+ class WeatherConditionItemData(BaseModel):
201
+ childType: WeatherConditionType
202
+ childData: ConditionItemChildData
203
+
204
+ @validator("childData", always=True)
205
+ def _check_child_data(cls, value, values):
206
+ childType = values.get("childType")
207
+ if childType == WeatherConditionType.irradiance:
208
+ assert value.data[0] > 0, ValueError
209
+ assert 0 <= value.data[1] <= 24, ValueError
210
+ return value
211
+
212
+ def to_text(self, lang, unit):
213
+ func = self.childData.function
214
+ data = self.childData.data
215
+ if self.childType == WeatherConditionType.irradiance:
216
+ return MESSAGE_ENTRY[self.childType][lang].format(
217
+ MESSAGE_ENTRY[str(func.value)][lang], data[0], data[1]
218
+ )
219
+ else:
220
+ return MESSAGE_ENTRY[self.childType][lang].format(
221
+ MESSAGE_ENTRY[str(func.value)][lang], data[0]
222
+ )
223
+
224
+
225
+ class ConditionItem(BaseModel):
226
+ type: ConditionType
227
+ cabinet: Optional[List[str]]
228
+ data: List[
229
+ Union[
230
+ DateConditionItemData,
231
+ WeatherConditionItemData,
232
+ PriceConditionItemData,
233
+ SystemConditionItemData,
234
+ CabinetConditionItemData,
235
+ ]
236
+ ]
237
+
238
+ @validator("cabinet")
239
+ def _check_cabinet(cls, value, values):
240
+ condition_type = values.get("type")
241
+ if condition_type == ConditionType.cabinet:
242
+ assert value, "cabinet is None"
243
+ return value
244
+
245
+ def to_text(self, lang, unit):
246
+ if self.type != ConditionType.cabinet:
247
+ return {self.type: [d.to_text(lang, unit) for d in self.data]}
248
+ elif self.type == ConditionType.cabinet:
249
+ cabinet_sns = ",".join(self.cabinet)
250
+ return {self.type: [d.to_text(lang, unit) for d in self.data] + [cabinet_sns]}
251
+
252
+
253
+ class SmartSceneCondition(BaseModel):
254
+ operation: LogicFunc
255
+ value: List[ConditionItem]
256
+
257
+ @root_validator
258
+ def _root_check(cls, values):
259
+ if values.get("operation") == LogicFunc.OR:
260
+ new_value = []
261
+ for item in values.get("value"):
262
+ if item.type != ConditionType.date:
263
+ new_value.append(item)
264
+ continue
265
+ new_data = []
266
+ for date_item in item.data:
267
+ if date_item.childType != DateConditionType.duration:
268
+ new_data.append(date_item)
269
+ item.data = new_data
270
+ if item.data:
271
+ new_value.append(item)
272
+ values["value"] = new_value
273
+ return values
274
+
275
+ def to_text(self, lang, unit):
276
+ # ret = {
277
+ # "operation": MESSAGE_ENTRY[self.operation.name][lang],
278
+ # "value": {}
279
+ # }
280
+ ret = {}
281
+ for v in self.value:
282
+ # ret["value"].update(v.to_text(lang, unit))
283
+ ret.update(v.to_text(lang, unit))
284
+ return ret
285
+
286
+ def get_duration_info(self):
287
+ for item in self.value:
288
+ if item.type != ConditionType.date:
289
+ continue
290
+ for date_item in item.data:
291
+ if date_item.childType == DateConditionType.duration:
292
+ return (date_item.childData.data[0] // 10) + 1
293
+ return 1