solax-py-library 1.0.0.2601__py3-none-any.whl → 1.0.0.2901__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.
- solax_py_library/device/types/device.py +10 -0
- solax_py_library/smart_scene/constant/message_entry.py +17 -5
- solax_py_library/smart_scene/core/action/base.py +1 -2
- solax_py_library/smart_scene/core/condition/cabinet_condition.py +2 -5
- solax_py_library/smart_scene/core/condition/weather_condition.py +8 -4
- solax_py_library/smart_scene/core/service/check.py +33 -0
- solax_py_library/smart_scene/types/action.py +177 -3
- solax_py_library/smart_scene/types/condition.py +35 -30
- solax_py_library/smart_scene/types/condition_value.py +6 -6
- solax_py_library/test/test_smart_scene/test_check_func.py +83 -0
- solax_py_library/test/test_smart_scene/test_condition.py +45 -24
- {solax_py_library-1.0.0.2601.dist-info → solax_py_library-1.0.0.2901.dist-info}/METADATA +1 -1
- {solax_py_library-1.0.0.2601.dist-info → solax_py_library-1.0.0.2901.dist-info}/RECORD +14 -12
- {solax_py_library-1.0.0.2601.dist-info → solax_py_library-1.0.0.2901.dist-info}/WHEEL +0 -0
@@ -14,3 +14,13 @@ class DeviceType(IntEnum):
|
|
14
14
|
FIRE_SAFETY_TYPE = 502
|
15
15
|
COLD_TYPE = 503
|
16
16
|
DEHUMIDIFY_TYPE = 504
|
17
|
+
|
18
|
+
def __str__(self):
|
19
|
+
return {
|
20
|
+
DeviceType.PCS_TYPE: "pcs",
|
21
|
+
DeviceType.BMS_TYPE: "bms",
|
22
|
+
DeviceType.ELM_TYPE: "elm",
|
23
|
+
DeviceType.IO_TYPE: "io",
|
24
|
+
DeviceType.AIRCONDITIONER_TYPE: "air_conditioner",
|
25
|
+
DeviceType.COLD_TYPE: "liquid_cooling_unit",
|
26
|
+
}.get(self)
|
@@ -159,21 +159,33 @@ MESSAGE_ENTRY = {
|
|
159
159
|
"duration": {"zh_CN": "持续: {}秒", "en_US": "last: {} seconds"},
|
160
160
|
"systemSoc": {"zh_CN": "系统soc {} {}%", "en_US": "system soc {} {}%"},
|
161
161
|
"systemImportPower": {
|
162
|
-
"zh_CN": "
|
163
|
-
"en_US": "
|
162
|
+
"zh_CN": "电网买电功率 {} {}KW",
|
163
|
+
"en_US": "Grid import power {} {}KW",
|
164
164
|
},
|
165
165
|
"systemExportPower": {
|
166
|
-
"zh_CN": "
|
167
|
-
"en_US": "
|
166
|
+
"zh_CN": "电网馈电功率 {} {}KW",
|
167
|
+
"en_US": "Grid export power {} {}KW",
|
168
168
|
},
|
169
169
|
"cabinetSoc": {"zh_CN": "机柜soc {} {}%", "en_US": "cabinet soc {} {}%"},
|
170
170
|
"EMERGENCY": {"zh_CN": "紧急告警", "en_US": "Emergency alarm"},
|
171
171
|
"TIPS": {"zh_CN": "状态提醒", "en_US": "State Tips"},
|
172
172
|
"NORMAL": {"zh_CN": "普通告警", "en_US": "Normal alarm"},
|
173
|
-
"cabinetAlarm": {
|
173
|
+
"cabinetAlarm": {
|
174
|
+
"zh_CN": "机柜下属({})设备发生{}",
|
175
|
+
"en_US": "The equipments({}) under the cabinet occurs {}",
|
176
|
+
},
|
174
177
|
"OR": {"zh_CN": "满足任一条件", "en_US": "Meet any of the conditions"},
|
175
178
|
"AND": {"zh_CN": "满足所有条件", "en_US": "Meet all conditions"},
|
176
179
|
"tips_alarm": {"zh_CN": "状态提醒", "en_US": "Alarm tips"},
|
177
180
|
"normal_alarm": {"zh_CN": "普通告警", "en_US": "Normal alarm"},
|
178
181
|
"emergency_alarm": {"zh_CN": "紧急告警", "en_US": "Emergency alarm"},
|
182
|
+
"pcs": {"zh_CN": "逆变器", "en_US": "Inverter"},
|
183
|
+
"bms": {"zh_CN": "电池", "en_US": "Battery"},
|
184
|
+
"elm": {
|
185
|
+
"zh_CN": "电表",
|
186
|
+
"en_US": "Meter",
|
187
|
+
},
|
188
|
+
"io": {"zh_CN": "IO模块", "en_US": "IO Module"},
|
189
|
+
"air_conditioner": {"zh_CN": "空调", "en_US": "Air conditioner"},
|
190
|
+
"liquid_cooling_unit": {"zh_CN": "液冷机组", "en_US": "Liquid Cooling Unit"},
|
179
191
|
}
|
@@ -6,7 +6,6 @@ from solax_py_library.smart_scene.types.condition import (
|
|
6
6
|
CabinetConditionType,
|
7
7
|
ConditionType,
|
8
8
|
)
|
9
|
-
from solax_py_library.device.types.alarm import AlarmLevel
|
10
9
|
from solax_py_library.smart_scene.types.condition_value import CabinetValue
|
11
10
|
|
12
11
|
|
@@ -15,9 +14,7 @@ class CabinetCondition(BaseCondition):
|
|
15
14
|
|
16
15
|
def __init__(self, update_value_function, **kwargs):
|
17
16
|
super().__init__(update_value_function, **kwargs)
|
18
|
-
self.value = defaultdict(
|
19
|
-
lambda: CabinetValue
|
20
|
-
)
|
17
|
+
self.value = defaultdict(lambda: CabinetValue)
|
21
18
|
|
22
19
|
def meet_func(self, data: CabinetConditionItemData, ctx):
|
23
20
|
if not self.value:
|
@@ -42,6 +39,6 @@ class CabinetCondition(BaseCondition):
|
|
42
39
|
alarm_info = cabinet_value.alarm_info(device_type)
|
43
40
|
if not alarm_info:
|
44
41
|
continue
|
45
|
-
if alarm_info[alarm_type-1] is True:
|
42
|
+
if alarm_info[alarm_type - 1] is True:
|
46
43
|
return True
|
47
44
|
return False
|
@@ -21,10 +21,14 @@ class WeatherCondition(BaseCondition):
|
|
21
21
|
function_value = child_data.function
|
22
22
|
data_value = child_data.data
|
23
23
|
nearest_time, right_time = get_rounded_times()
|
24
|
-
if
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
if (
|
25
|
+
nearest_time not in self.value["timeList"]
|
26
|
+
and right_time not in self.value["timeList"]
|
27
|
+
):
|
28
|
+
return False
|
29
|
+
time_now = (
|
30
|
+
right_time if nearest_time not in self.value["timeList"] else nearest_time
|
31
|
+
)
|
28
32
|
index = self.value["timeList"].index(time_now)
|
29
33
|
if child_type == WeatherConditionType.irradiance:
|
30
34
|
return self.meet_func_irradiance(function_value, data_value, index)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from typing import List, Dict, Any
|
2
|
+
|
3
|
+
from solax_py_library.smart_scene.types.action import SmartSceneAction, ActionType
|
4
|
+
from solax_py_library.smart_scene.types.condition import (
|
5
|
+
SmartSceneCondition,
|
6
|
+
ConditionType,
|
7
|
+
)
|
8
|
+
|
9
|
+
|
10
|
+
def action_param_check(actions: List[SmartSceneAction], ctx: Dict[str, Any]):
|
11
|
+
"""动作里的参数范围判定"""
|
12
|
+
for action in actions:
|
13
|
+
if action.type != ActionType.system:
|
14
|
+
continue
|
15
|
+
for action_data in action.data:
|
16
|
+
ret = action_data.check_param(ctx)
|
17
|
+
if ret is not None:
|
18
|
+
return ret
|
19
|
+
return True
|
20
|
+
|
21
|
+
|
22
|
+
def condition_param_check(condition: SmartSceneCondition, ctx: Dict[str, Any]):
|
23
|
+
for condition_data in condition.value:
|
24
|
+
if condition_data.type not in [
|
25
|
+
ConditionType.systemCondition,
|
26
|
+
ConditionType.cabinet,
|
27
|
+
]:
|
28
|
+
continue
|
29
|
+
for data in condition_data.data:
|
30
|
+
ret = data.check_param(ctx)
|
31
|
+
if ret is not None:
|
32
|
+
return ret
|
33
|
+
return True
|
@@ -1,10 +1,28 @@
|
|
1
|
-
from enum import Enum
|
1
|
+
from enum import Enum, IntEnum
|
2
2
|
from typing import List, Union, Any
|
3
3
|
|
4
4
|
from pydantic import BaseModel
|
5
5
|
|
6
6
|
from solax_py_library.device.constant.cabinet import TRENE_CABINET_ENUM
|
7
7
|
from solax_py_library.smart_scene.constant.message_entry import MESSAGE_ENTRY
|
8
|
+
from solax_py_library.smart_scene.exceptions.smart_scene import (
|
9
|
+
ExportLimitNum,
|
10
|
+
ImportLimitNum,
|
11
|
+
ImportOnlyPositive,
|
12
|
+
PowerLimitNum,
|
13
|
+
SocLimit,
|
14
|
+
ActivePowerLimitNum,
|
15
|
+
ReactivePowerLimitNum,
|
16
|
+
EnergyLimit,
|
17
|
+
BatteryPowerLimitNum,
|
18
|
+
PvOnlyGe0,
|
19
|
+
ExportLimitPercent,
|
20
|
+
)
|
21
|
+
|
22
|
+
|
23
|
+
class SmartSceneUnit(IntEnum):
|
24
|
+
PERCENT = 1
|
25
|
+
NUM = 2
|
8
26
|
|
9
27
|
|
10
28
|
class ActionType(str, Enum):
|
@@ -36,7 +54,12 @@ class SystemActionChildData(ActionChildData):
|
|
36
54
|
...
|
37
55
|
|
38
56
|
|
39
|
-
class
|
57
|
+
class ActionItemData(BaseModel):
|
58
|
+
def check_param(self, ctx):
|
59
|
+
...
|
60
|
+
|
61
|
+
|
62
|
+
class SystemActionItemData(ActionItemData):
|
40
63
|
childType: SystemActionType
|
41
64
|
childData: SystemActionChildData
|
42
65
|
|
@@ -136,12 +159,163 @@ class SystemActionItemData(BaseModel):
|
|
136
159
|
return MESSAGE_ENTRY[work_mode[value_data[0]]][lang]
|
137
160
|
return ""
|
138
161
|
|
162
|
+
def check_param(self, ctx):
|
163
|
+
if self.childType == SystemActionType.exportControl:
|
164
|
+
export_power_top_limit = ctx.pop("export_power_top_limit")
|
165
|
+
switch = self.childData.data[0]
|
166
|
+
if not switch:
|
167
|
+
return
|
168
|
+
_, _, value, unit = self.childData.data
|
169
|
+
if unit == SmartSceneUnit.NUM:
|
170
|
+
if value > export_power_top_limit or value < 0:
|
171
|
+
return ExportLimitNum, {"up_limit": export_power_top_limit}
|
172
|
+
else:
|
173
|
+
if value > 0 or value < 110:
|
174
|
+
return ExportLimitPercent, {}
|
175
|
+
elif self.childType == SystemActionType.importControl:
|
176
|
+
import_power_top_limit = ctx.pop("import_power_top_limit", None)
|
177
|
+
total_power_top_limit = ctx.pop("total_power_top_limit", None)
|
178
|
+
switch = self.childData.data[0]
|
179
|
+
if not switch:
|
180
|
+
return
|
181
|
+
value = self.childData.data[-1]
|
182
|
+
if import_power_top_limit is not None:
|
183
|
+
if value > total_power_top_limit or value < 0:
|
184
|
+
return (
|
185
|
+
ImportLimitNum.message,
|
186
|
+
{"up_limit": import_power_top_limit},
|
187
|
+
)
|
188
|
+
else:
|
189
|
+
if value < 0:
|
190
|
+
return ImportOnlyPositive.message, {}
|
191
|
+
elif self.childType == SystemActionType.workMode:
|
192
|
+
work_mode = self.childData.data[0]
|
193
|
+
total_power_top_limit = ctx.pop("total_power_top_limit")
|
194
|
+
total_energy_top_limit = ctx.pop("total_energy_top_limit")
|
195
|
+
soc_low_limit = ctx.pop("soc_low_limit")
|
196
|
+
if work_mode == 3: # 手动模式
|
197
|
+
if self.childData.data[1] in [3, 4]: # 强充或强放
|
198
|
+
_, _, power, soc = self.childData.data
|
199
|
+
if power <= 0 or power > total_power_top_limit:
|
200
|
+
return (
|
201
|
+
PowerLimitNum.message,
|
202
|
+
{"low_limit": 0, "up_limit": total_power_top_limit},
|
203
|
+
)
|
204
|
+
if soc > 100 or soc < soc_low_limit:
|
205
|
+
return (
|
206
|
+
SocLimit.message,
|
207
|
+
{"low_limit": soc_low_limit},
|
208
|
+
)
|
209
|
+
elif work_mode == 16: # VPP模式
|
210
|
+
vpp_mode = self.childData.data[1]
|
211
|
+
if vpp_mode == 1:
|
212
|
+
(
|
213
|
+
_,
|
214
|
+
_,
|
215
|
+
active_power,
|
216
|
+
reactive_power,
|
217
|
+
) = self.childData.data
|
218
|
+
if (
|
219
|
+
active_power < -total_power_top_limit
|
220
|
+
or active_power > total_power_top_limit
|
221
|
+
):
|
222
|
+
return (
|
223
|
+
ActivePowerLimitNum.message,
|
224
|
+
{
|
225
|
+
"low_limit": -total_power_top_limit,
|
226
|
+
"up_limit": total_power_top_limit,
|
227
|
+
},
|
228
|
+
)
|
229
|
+
if (
|
230
|
+
reactive_power < -total_power_top_limit
|
231
|
+
or reactive_power > total_power_top_limit
|
232
|
+
):
|
233
|
+
return (
|
234
|
+
ReactivePowerLimitNum.message,
|
235
|
+
{
|
236
|
+
"low_limit": -total_power_top_limit,
|
237
|
+
"up_limit": total_power_top_limit,
|
238
|
+
},
|
239
|
+
)
|
240
|
+
elif vpp_mode == 2:
|
241
|
+
_, _, energy, power = self.childData.data
|
242
|
+
if energy < 0 or energy > total_energy_top_limit:
|
243
|
+
return (
|
244
|
+
EnergyLimit.message,
|
245
|
+
{"up_limit": total_energy_top_limit},
|
246
|
+
)
|
247
|
+
if power < -total_power_top_limit or power > total_power_top_limit:
|
248
|
+
return (
|
249
|
+
PowerLimitNum.message,
|
250
|
+
{
|
251
|
+
"low_limit": -total_power_top_limit,
|
252
|
+
"up_limit": total_power_top_limit,
|
253
|
+
},
|
254
|
+
)
|
255
|
+
elif vpp_mode == 3:
|
256
|
+
_, _, soc, power = self.childData.data
|
257
|
+
if soc < soc_low_limit or soc > 100:
|
258
|
+
return (
|
259
|
+
SocLimit.message,
|
260
|
+
{"low_limit": soc_low_limit},
|
261
|
+
)
|
262
|
+
if power < -total_power_top_limit or power > total_power_top_limit:
|
263
|
+
return (
|
264
|
+
PowerLimitNum.message,
|
265
|
+
{
|
266
|
+
"low_limit": -total_power_top_limit,
|
267
|
+
"up_limit": total_power_top_limit,
|
268
|
+
},
|
269
|
+
)
|
270
|
+
elif vpp_mode == 4:
|
271
|
+
_, _, power = self.childData.data
|
272
|
+
if power < -total_power_top_limit or power > total_power_top_limit:
|
273
|
+
return (
|
274
|
+
BatteryPowerLimitNum.message,
|
275
|
+
{
|
276
|
+
"low_limit": -total_power_top_limit,
|
277
|
+
"up_limit": total_power_top_limit,
|
278
|
+
},
|
279
|
+
)
|
280
|
+
elif vpp_mode == 8:
|
281
|
+
_, _, pv_power, bms_power = self.childData.data
|
282
|
+
if pv_power < 0:
|
283
|
+
return (PvOnlyGe0.message, {})
|
284
|
+
elif (
|
285
|
+
bms_power < -total_power_top_limit
|
286
|
+
or bms_power > total_power_top_limit
|
287
|
+
):
|
288
|
+
return (
|
289
|
+
BatteryPowerLimitNum.message,
|
290
|
+
{
|
291
|
+
"low_limit": -total_power_top_limit,
|
292
|
+
"up_limit": total_power_top_limit,
|
293
|
+
},
|
294
|
+
)
|
295
|
+
elif vpp_mode == 9:
|
296
|
+
_, _, pv_power, bms_power, soc = self.childData.data
|
297
|
+
if pv_power < 0:
|
298
|
+
return (PvOnlyGe0.message, {})
|
299
|
+
if (
|
300
|
+
bms_power < -total_power_top_limit
|
301
|
+
or bms_power > total_power_top_limit
|
302
|
+
):
|
303
|
+
return (
|
304
|
+
BatteryPowerLimitNum.message,
|
305
|
+
{
|
306
|
+
"low_limit": -total_power_top_limit,
|
307
|
+
"up_limit": total_power_top_limit,
|
308
|
+
},
|
309
|
+
)
|
310
|
+
if soc < soc_low_limit or soc > 100:
|
311
|
+
return SocLimit.message, {"low_limit": soc_low_limit}
|
312
|
+
|
139
313
|
|
140
314
|
class EmsActionChildData(ActionChildData):
|
141
315
|
data: List[DoControl]
|
142
316
|
|
143
317
|
|
144
|
-
class EmsActionItemData(
|
318
|
+
class EmsActionItemData(ActionItemData):
|
145
319
|
childType: EmsActionType
|
146
320
|
childData: EmsActionChildData
|
147
321
|
|
@@ -2,10 +2,12 @@ import operator
|
|
2
2
|
from enum import IntEnum, Enum
|
3
3
|
from typing import Optional, List, Union, Any
|
4
4
|
|
5
|
-
from pydantic import BaseModel, validator
|
5
|
+
from pydantic import BaseModel, validator
|
6
6
|
|
7
7
|
from solax_py_library.device.types.alarm import AlarmLevel
|
8
|
+
from solax_py_library.device.types.device import DeviceType
|
8
9
|
from solax_py_library.smart_scene.constant.message_entry import MESSAGE_ENTRY
|
10
|
+
from solax_py_library.smart_scene.exceptions.smart_scene import SocLimit
|
9
11
|
|
10
12
|
|
11
13
|
class LogicFunc(IntEnum):
|
@@ -82,7 +84,12 @@ class ConditionItemChildData(BaseModel):
|
|
82
84
|
data: List[Any]
|
83
85
|
|
84
86
|
|
85
|
-
class
|
87
|
+
class ConditionItemData(BaseModel):
|
88
|
+
def check_param(self, ctx):
|
89
|
+
...
|
90
|
+
|
91
|
+
|
92
|
+
class PriceConditionItemData(ConditionItemData):
|
86
93
|
childType: PriceConditionType
|
87
94
|
childData: ConditionItemChildData
|
88
95
|
|
@@ -122,7 +129,7 @@ class PriceConditionItemData(BaseModel):
|
|
122
129
|
return MESSAGE_ENTRY[self.childType][lang].format(data[0], data[1], data[2])
|
123
130
|
|
124
131
|
|
125
|
-
class SystemConditionItemData(
|
132
|
+
class SystemConditionItemData(ConditionItemData):
|
126
133
|
childType: SystemConditionType
|
127
134
|
childData: ConditionItemChildData
|
128
135
|
|
@@ -135,8 +142,6 @@ class SystemConditionItemData(BaseModel):
|
|
135
142
|
}:
|
136
143
|
assert 0 <= value.data[0] <= 100000, ValueError
|
137
144
|
value.data[0] = round(value.data[0], 2) # 功率保留两位小数
|
138
|
-
elif child_type == SystemConditionType.systemSoc:
|
139
|
-
assert 5 <= value.data[0] <= 100, ValueError
|
140
145
|
return value
|
141
146
|
|
142
147
|
def to_text(self, lang, unit):
|
@@ -154,8 +159,15 @@ class SystemConditionItemData(BaseModel):
|
|
154
159
|
MESSAGE_ENTRY[str(func.value)][lang], data[0]
|
155
160
|
)
|
156
161
|
|
162
|
+
def check_param(self, ctx):
|
163
|
+
soc_low_limit = ctx.pop("soc_low_limit", 5)
|
164
|
+
if self.childType == SystemConditionType.systemSoc:
|
165
|
+
soc = self.childData.data[0]
|
166
|
+
if soc < soc_low_limit or soc > 100:
|
167
|
+
return SocLimit.message, {"low_limit": soc_low_limit}
|
168
|
+
|
157
169
|
|
158
|
-
class CabinetConditionItemData(
|
170
|
+
class CabinetConditionItemData(ConditionItemData):
|
159
171
|
childType: CabinetConditionType
|
160
172
|
childData: ConditionItemChildData
|
161
173
|
|
@@ -168,8 +180,6 @@ class CabinetConditionItemData(BaseModel):
|
|
168
180
|
AlarmLevel.NORMAL,
|
169
181
|
AlarmLevel.EMERGENCY,
|
170
182
|
}, ValueError
|
171
|
-
if child_type == CabinetConditionType.cabinetSoc:
|
172
|
-
assert 0 <= value.data[0] <= 100, ValueError
|
173
183
|
return value
|
174
184
|
|
175
185
|
def to_text(self, lang, unit):
|
@@ -181,16 +191,29 @@ class CabinetConditionItemData(BaseModel):
|
|
181
191
|
)
|
182
192
|
elif self.childType == CabinetConditionType.cabinetAlarm:
|
183
193
|
return MESSAGE_ENTRY[self.childType][lang].format(
|
184
|
-
|
194
|
+
",".join(
|
195
|
+
[
|
196
|
+
MESSAGE_ENTRY[str(DeviceType(device_type))][lang]
|
197
|
+
for device_type in data[:-1]
|
198
|
+
]
|
199
|
+
),
|
200
|
+
MESSAGE_ENTRY[str(AlarmLevel(data[-1]))][lang],
|
185
201
|
)
|
186
202
|
|
203
|
+
def check_param(self, ctx):
|
204
|
+
soc_low_limit = ctx.pop("soc_low_limit", 5)
|
205
|
+
if self.childType == CabinetConditionType.cabinetSoc:
|
206
|
+
soc = self.childData.data[0]
|
207
|
+
if soc < soc_low_limit or soc > 100:
|
208
|
+
return SocLimit.message, {"low_limit": soc_low_limit}
|
209
|
+
|
187
210
|
|
188
|
-
class DateConditionItemData(
|
211
|
+
class DateConditionItemData(ConditionItemData):
|
189
212
|
childType: DateConditionType
|
190
213
|
childData: ConditionItemChildData
|
191
214
|
|
192
215
|
@validator("childData", always=True)
|
193
|
-
def
|
216
|
+
def _check_child_data(cls, value, values):
|
194
217
|
child_type = values.get("childType")
|
195
218
|
data = value.data
|
196
219
|
if child_type == DateConditionType.time:
|
@@ -206,7 +229,7 @@ class DateConditionItemData(BaseModel):
|
|
206
229
|
return MESSAGE_ENTRY[self.childType][lang] + "/" + self.childData.data[0]
|
207
230
|
|
208
231
|
|
209
|
-
class WeatherConditionItemData(
|
232
|
+
class WeatherConditionItemData(ConditionItemData):
|
210
233
|
childType: WeatherConditionType
|
211
234
|
childData: ConditionItemChildData
|
212
235
|
|
@@ -265,24 +288,6 @@ class SmartSceneCondition(BaseModel):
|
|
265
288
|
operation: LogicFunc
|
266
289
|
value: List[ConditionItem]
|
267
290
|
|
268
|
-
@root_validator
|
269
|
-
def _root_check(cls, values):
|
270
|
-
if values.get("operation") == LogicFunc.OR:
|
271
|
-
new_value = []
|
272
|
-
for item in values.get("value"):
|
273
|
-
if item.type != ConditionType.date:
|
274
|
-
new_value.append(item)
|
275
|
-
continue
|
276
|
-
new_data = []
|
277
|
-
for date_item in item.data:
|
278
|
-
if date_item.childType != DateConditionType.duration:
|
279
|
-
new_data.append(date_item)
|
280
|
-
item.data = new_data
|
281
|
-
if item.data:
|
282
|
-
new_value.append(item)
|
283
|
-
values["value"] = new_value
|
284
|
-
return values
|
285
|
-
|
286
291
|
def to_text(self, lang, unit):
|
287
292
|
ret = {"operation": [MESSAGE_ENTRY[self.operation.name][lang]]}
|
288
293
|
for v in self.value:
|
@@ -7,12 +7,12 @@ from solax_py_library.device.types.device import DeviceType
|
|
7
7
|
|
8
8
|
class CabinetValue(BaseModel):
|
9
9
|
soc: int = None
|
10
|
-
cabinet_alarm: List[bool] = Field(default_factory=lambda
|
11
|
-
pcs_alarm:
|
12
|
-
io_alarm:
|
13
|
-
bms_alarm:
|
14
|
-
air_alarm:
|
15
|
-
liquid_alarm:
|
10
|
+
cabinet_alarm: List[bool] = Field(default_factory=lambda: [False, False, False])
|
11
|
+
pcs_alarm: List[bool] = Field(default_factory=lambda: [False, False, False])
|
12
|
+
io_alarm: List[bool] = Field(default_factory=lambda: [False, False, False])
|
13
|
+
bms_alarm: List[bool] = Field(default_factory=lambda: [False, False, False])
|
14
|
+
air_alarm: List[bool] = Field(default_factory=lambda: [False, False, False])
|
15
|
+
liquid_alarm: List[bool] = Field(default_factory=lambda: [False, False, False])
|
16
16
|
|
17
17
|
def alarm_info(self, device_type):
|
18
18
|
if device_type == DeviceType.EMS_TYPE:
|
@@ -0,0 +1,83 @@
|
|
1
|
+
from unittest import TestCase
|
2
|
+
|
3
|
+
from solax_py_library.smart_scene.core.service.check import (
|
4
|
+
condition_param_check,
|
5
|
+
action_param_check,
|
6
|
+
)
|
7
|
+
from solax_py_library.smart_scene.types.action import (
|
8
|
+
SmartSceneAction,
|
9
|
+
ActionType,
|
10
|
+
SystemActionType,
|
11
|
+
SystemActionItemData,
|
12
|
+
SystemActionChildData,
|
13
|
+
)
|
14
|
+
from solax_py_library.smart_scene.types.condition import (
|
15
|
+
SmartSceneCondition,
|
16
|
+
ConditionItem,
|
17
|
+
ConditionType,
|
18
|
+
SystemConditionItemData,
|
19
|
+
LogicFunc,
|
20
|
+
SystemConditionType,
|
21
|
+
ConditionItemChildData,
|
22
|
+
CabinetConditionItemData,
|
23
|
+
CabinetConditionType,
|
24
|
+
ConditionFunc,
|
25
|
+
)
|
26
|
+
|
27
|
+
|
28
|
+
class TestCheckFunc(TestCase):
|
29
|
+
def test_check_condition_func(self):
|
30
|
+
con = SmartSceneCondition(
|
31
|
+
operation=LogicFunc.AND,
|
32
|
+
value=[
|
33
|
+
ConditionItem(
|
34
|
+
type=ConditionType.systemCondition,
|
35
|
+
data=[
|
36
|
+
SystemConditionItemData(
|
37
|
+
childType=SystemConditionType.systemSoc,
|
38
|
+
childData=ConditionItemChildData(
|
39
|
+
data=[101], function=ConditionFunc.EQ
|
40
|
+
),
|
41
|
+
),
|
42
|
+
],
|
43
|
+
cabinet=None,
|
44
|
+
),
|
45
|
+
ConditionItem(
|
46
|
+
type=ConditionType.cabinet,
|
47
|
+
data=[
|
48
|
+
CabinetConditionItemData(
|
49
|
+
childType=CabinetConditionType.cabinetSoc,
|
50
|
+
childData=ConditionItemChildData(
|
51
|
+
data=[4], function=ConditionFunc.EQ
|
52
|
+
),
|
53
|
+
)
|
54
|
+
],
|
55
|
+
cabinet=["1", "2"],
|
56
|
+
),
|
57
|
+
],
|
58
|
+
)
|
59
|
+
ret = condition_param_check(con, ctx={"soc_low_limit": 10})
|
60
|
+
assert ret is not None
|
61
|
+
|
62
|
+
def test_check_action_func(self):
|
63
|
+
action = [
|
64
|
+
SmartSceneAction(
|
65
|
+
type=ActionType.system,
|
66
|
+
data=[
|
67
|
+
SystemActionItemData(
|
68
|
+
childType=SystemActionType.importControl,
|
69
|
+
childData=SystemActionChildData(data=[1, -10]),
|
70
|
+
)
|
71
|
+
],
|
72
|
+
)
|
73
|
+
]
|
74
|
+
ret = action_param_check(
|
75
|
+
actions=action,
|
76
|
+
ctx={
|
77
|
+
"cabinet_type": 3,
|
78
|
+
"total_power": 1,
|
79
|
+
"total_power_except_solar_inv": 1,
|
80
|
+
"total_energy": 1,
|
81
|
+
},
|
82
|
+
)
|
83
|
+
assert ret is not None
|
@@ -1,20 +1,36 @@
|
|
1
1
|
from unittest import TestCase
|
2
2
|
|
3
3
|
from solax_py_library.device.types.device import DeviceType
|
4
|
-
from solax_py_library.smart_scene.core.condition import
|
5
|
-
|
6
|
-
|
4
|
+
from solax_py_library.smart_scene.core.condition import (
|
5
|
+
DateCondition,
|
6
|
+
BaseCondition,
|
7
|
+
CabinetCondition,
|
8
|
+
)
|
9
|
+
from solax_py_library.smart_scene.types.condition import (
|
10
|
+
CabinetConditionItemData,
|
11
|
+
CabinetConditionType,
|
12
|
+
ConditionItemChildData,
|
13
|
+
)
|
7
14
|
from solax_py_library.smart_scene.types.condition_value import CabinetValue
|
8
15
|
|
9
16
|
|
10
17
|
class TestCondition(TestCase):
|
11
|
-
def
|
18
|
+
def test_condition_build(self):
|
12
19
|
date_condition = DateCondition(
|
13
20
|
update_value_function=lambda: 1,
|
14
21
|
)
|
15
22
|
assert isinstance(date_condition, BaseCondition)
|
16
23
|
|
17
|
-
def
|
24
|
+
def test_cabinet_condition_to_text(self):
|
25
|
+
cabinet_condition = CabinetConditionItemData(
|
26
|
+
childType=CabinetConditionType.cabinetAlarm,
|
27
|
+
childData=ConditionItemChildData(
|
28
|
+
data=[DeviceType.IO_TYPE, DeviceType.COLD_TYPE, 1]
|
29
|
+
),
|
30
|
+
)
|
31
|
+
print(cabinet_condition.to_text(lang="zh_CN", unit="嘻嘻"))
|
32
|
+
|
33
|
+
def test_cabinet_condition_check(self):
|
18
34
|
cabinet_condition = CabinetCondition(
|
19
35
|
update_value_function=lambda: {
|
20
36
|
"SN1": CabinetValue(
|
@@ -24,22 +40,27 @@ class TestCondition(TestCase):
|
|
24
40
|
},
|
25
41
|
)
|
26
42
|
cabinet_condition.update_value()
|
27
|
-
assert
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
assert (
|
44
|
+
cabinet_condition.meet_func(
|
45
|
+
data=CabinetConditionItemData(
|
46
|
+
childType=CabinetConditionType.cabinetAlarm,
|
47
|
+
childData=ConditionItemChildData(
|
48
|
+
data=[DeviceType.IO_TYPE, DeviceType.COLD_TYPE, 1]
|
49
|
+
),
|
50
|
+
),
|
51
|
+
ctx={"cabinet": ["SN1"]},
|
52
|
+
)
|
53
|
+
is False
|
54
|
+
)
|
55
|
+
assert (
|
56
|
+
cabinet_condition.meet_func(
|
57
|
+
data=CabinetConditionItemData(
|
58
|
+
childType=CabinetConditionType.cabinetAlarm,
|
59
|
+
childData=ConditionItemChildData(
|
60
|
+
data=[DeviceType.IO_TYPE, DeviceType.COLD_TYPE, 2]
|
61
|
+
),
|
62
|
+
),
|
63
|
+
ctx={"cabinet": ["SN1"]},
|
64
|
+
)
|
65
|
+
is True
|
66
|
+
)
|
@@ -8,35 +8,36 @@ solax_py_library/device/core/interver/__init__.py,sha256=RKye2D6NawSGdL4YUp_H-Lm
|
|
8
8
|
solax_py_library/device/core/interver/base.py,sha256=2TXHsjigMcIvGDLF3ZD4dw6UDrRRAk9Mq6sdBKRvydc,7191
|
9
9
|
solax_py_library/device/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
solax_py_library/device/types/alarm.py,sha256=kartXs_iRSV9Y3weFSsh9wMXU1_FxHZa6inHThyQCOk,358
|
11
|
-
solax_py_library/device/types/device.py,sha256=
|
11
|
+
solax_py_library/device/types/device.py,sha256=oGnuJXPpoOFUc3LgrwCMJSAXKX6VQYEjEd_VmU6zcBA,667
|
12
12
|
solax_py_library/device/types/inverter_config.py,sha256=qCInNPbgsWf6yQjSw59kfQtJJWilMYUhvx_qo5qwRlU,912
|
13
13
|
solax_py_library/device/types/modbus_point.py,sha256=YmXe92gWXL_voVXDJE5zzNzr6dpPs7Ff3ciOAW-LgPs,580
|
14
14
|
solax_py_library/exception.py,sha256=ygAccdTqJctRrdt9bu6-vqZP5KadfKVS_1tjt4KcRn8,257
|
15
15
|
solax_py_library/smart_scene/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
16
|
solax_py_library/smart_scene/constant/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
|
-
solax_py_library/smart_scene/constant/message_entry.py,sha256=
|
17
|
+
solax_py_library/smart_scene/constant/message_entry.py,sha256=4dItLHwgYjSvEb-UbhjT1l8BIbNGk9EyTNhrqD4HEBY,9667
|
18
18
|
solax_py_library/smart_scene/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
19
|
solax_py_library/smart_scene/core/action/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
-
solax_py_library/smart_scene/core/action/base.py,sha256=
|
20
|
+
solax_py_library/smart_scene/core/action/base.py,sha256=GlZQTi7ub0fREV5BUZQUTARw3mleWWsKHOA7eFPVoJs,319
|
21
21
|
solax_py_library/smart_scene/core/action/ems_action.py,sha256=sML6qasFoqOktTvEcHm0vKPYCj60VcKjAFz8RAaQc2U,206
|
22
22
|
solax_py_library/smart_scene/core/action/system_action.py,sha256=oGXq3yXS9nKcGjJActjk0R2Wr3AoO9uoyRPyuiM053g,204
|
23
23
|
solax_py_library/smart_scene/core/condition/__init__.py,sha256=1nN-N52Oq7LKdn6ApKGtSZq5fB1qJzJq8BOKOumfQvY,475
|
24
24
|
solax_py_library/smart_scene/core/condition/base.py,sha256=saj7dc0Su2Wi_Lx04cesHFgIPDyQUwvHuDElcaDOIHU,596
|
25
|
-
solax_py_library/smart_scene/core/condition/cabinet_condition.py,sha256=
|
25
|
+
solax_py_library/smart_scene/core/condition/cabinet_condition.py,sha256=HhOmFGs6srnC4oO5h1p3Z09Y9LPeywi1KaL5dG0pJf8,1724
|
26
26
|
solax_py_library/smart_scene/core/condition/date_condition.py,sha256=Xhca6VjoM8Bq-I-dFj1RPLTTzbBL81ORkBnR8D-YqUw,772
|
27
27
|
solax_py_library/smart_scene/core/condition/price_condition.py,sha256=IkgoB5YhpMxgFVkabilcBXtkjsqae01kkjF3tH10CK0,4006
|
28
28
|
solax_py_library/smart_scene/core/condition/system_condition.py,sha256=q5KDQdK6wjEvq0__WwBR4Sk-59yA2aIAgxTf1xjxJQk,1338
|
29
|
-
solax_py_library/smart_scene/core/condition/weather_condition.py,sha256=
|
29
|
+
solax_py_library/smart_scene/core/condition/weather_condition.py,sha256=ZoP5QM0kswQCrb1N22_W358BnhgDc6eXp9XPR5WKAgs,2363
|
30
30
|
solax_py_library/smart_scene/core/service/__init__.py,sha256=wWzHSN2XaHnI-TNtCJWWRHnNC7s3-2GNQo9y0K_PC4Q,69
|
31
|
+
solax_py_library/smart_scene/core/service/check.py,sha256=qoNixyjgwHRdb0HnU3UcZDEOeI9t0PxvjZcbgvB79Vs,1031
|
31
32
|
solax_py_library/smart_scene/core/service/runner.py,sha256=SwQ6jb5yFPcyHyfU-THyGDjPEMcNFUOHkvVYA9wB1EE,6201
|
32
33
|
solax_py_library/smart_scene/exceptions/__init__.py,sha256=0hDgr70fFLQB14uorVCwbBhl1yQmZ-uBYGH5XtGm_dg,147
|
33
34
|
solax_py_library/smart_scene/exceptions/price.py,sha256=3bnY6JzeEskUoXVzEs8bpg6hQzgbinBKY4GP4hBITWU,152
|
34
35
|
solax_py_library/smart_scene/exceptions/smart_scene.py,sha256=69khvoFm1Eki4NBT45gVnsyWubEzF7dqnhU-unqT20g,1701
|
35
36
|
solax_py_library/smart_scene/exceptions/weather.py,sha256=bJl1VwiIXEpLQ9VjlVrDoTAIMFqVZdRCas7dtR7eAJc,133
|
36
37
|
solax_py_library/smart_scene/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
|
-
solax_py_library/smart_scene/types/action.py,sha256=
|
38
|
-
solax_py_library/smart_scene/types/condition.py,sha256=
|
39
|
-
solax_py_library/smart_scene/types/condition_value.py,sha256=
|
38
|
+
solax_py_library/smart_scene/types/action.py,sha256=Xne9t-g-Ob5Le06zfKO7ceP9dtdMeq7EFcFske7GbmM,13227
|
39
|
+
solax_py_library/smart_scene/types/condition.py,sha256=etBD6hQHxyhtYucvIhKp0F1rTnaC6D2IEwQyzyuTzgY,9730
|
40
|
+
solax_py_library/smart_scene/types/condition_value.py,sha256=by5R3sVNOJCrghLLaNT54jiTqZYT7YvQQ4q9ovIXkqQ,1242
|
40
41
|
solax_py_library/smart_scene/types/smart_scene_content.py,sha256=C8H17QEicmDBbxN-m550njwaZyUhAL2hUhlLg3Qj1zM,6061
|
41
42
|
solax_py_library/snap_shot/__init__.py,sha256=Ex12q6BCkdU-3OP-f-ehGCetJJWnoZ7KxhEDd_lXh6M,81
|
42
43
|
solax_py_library/snap_shot/constant/__init__.py,sha256=UNfjAlx1wovXc1oH74af9oIe2TljwCCiTzNXzWgtUms,65
|
@@ -52,7 +53,8 @@ solax_py_library/snap_shot/types/__init__.py,sha256=g9ybB88TntvAMGIhLgJ31Xxn26zl
|
|
52
53
|
solax_py_library/snap_shot/types/address.py,sha256=JhyB-t2OnKuE8akKk120sojCNXv4_OlLLuWsl5ChFZ8,1148
|
53
54
|
solax_py_library/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
55
|
solax_py_library/test/test_smart_scene/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
|
-
solax_py_library/test/test_smart_scene/
|
56
|
+
solax_py_library/test/test_smart_scene/test_check_func.py,sha256=mVZedGT2OocqX1-qtVlVHRlimwyR3xtRwxcOGzREZAE,2584
|
57
|
+
solax_py_library/test/test_smart_scene/test_condition.py,sha256=du1vqtwmDFPY__SNqQ-RcDjrDO6e2ZnBjNbPxWpihI4,2209
|
56
58
|
solax_py_library/test/test_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
59
|
solax_py_library/test/test_utils/test_cloud_client.py,sha256=gOrHGXkFXpFV4kXTnjhmyJGem8VaGKw8OmXyW884oJ0,395
|
58
60
|
solax_py_library/upload/__init__.py,sha256=XhZar7BKaRN0XcdPl4QffWr488L3UWvuq5syT8nX2OU,93
|
@@ -77,6 +79,6 @@ solax_py_library/utils/cloud_client.py,sha256=5dZrc5fzrNFSXqTPZd7oHt-Y9Jj6RCigB7
|
|
77
79
|
solax_py_library/utils/common.py,sha256=bfnZcX9uM-PjJrYAFv1UMmZgt6bGR7MaOd7jRPNHGxw,1238
|
78
80
|
solax_py_library/utils/struct_util.py,sha256=pL6L80GXIHasy1ZDIj89-5BzXW1BWI3TPitH7thGGIE,1577
|
79
81
|
solax_py_library/utils/time_util.py,sha256=bY5kj9dmyOuLEQ6uYGQK7jU7y1RMiHZgevEKnkcQcSU,1461
|
80
|
-
solax_py_library-1.0.0.
|
81
|
-
solax_py_library-1.0.0.
|
82
|
-
solax_py_library-1.0.0.
|
82
|
+
solax_py_library-1.0.0.2901.dist-info/METADATA,sha256=o28wtYRXED7pZtI7DX-wNJUdvCCyXnOK4btb4JVKiUc,1827
|
83
|
+
solax_py_library-1.0.0.2901.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
84
|
+
solax_py_library-1.0.0.2901.dist-info/RECORD,,
|
File without changes
|