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.
- solax_py_library/__init__.py +1 -1
- solax_py_library/device/constant/inverter_model_info.py +312 -312
- solax_py_library/device/core/interver/__init__.py +36 -36
- solax_py_library/device/core/interver/base.py +215 -215
- solax_py_library/device/types/inverter_config.py +41 -41
- solax_py_library/device/types/modbus_point.py +30 -30
- solax_py_library/exception.py +10 -10
- solax_py_library/smart_scene/__init__.py +0 -0
- solax_py_library/smart_scene/constant/__init__.py +0 -0
- solax_py_library/smart_scene/constant/message_entry.py +338 -0
- solax_py_library/smart_scene/core/__init__.py +0 -0
- solax_py_library/smart_scene/core/action/__init__.py +0 -0
- solax_py_library/smart_scene/core/condition/__init__.py +0 -0
- solax_py_library/smart_scene/core/condition/base.py +12 -0
- solax_py_library/smart_scene/core/condition/cabinet_condition.py +104 -0
- solax_py_library/smart_scene/core/condition/date_condition.py +26 -0
- solax_py_library/smart_scene/core/condition/price_condition.py +105 -0
- solax_py_library/smart_scene/core/condition/system_condition.py +41 -0
- solax_py_library/smart_scene/core/condition/weather_condition.py +67 -0
- solax_py_library/smart_scene/exceptions/__init__.py +0 -0
- solax_py_library/smart_scene/exceptions/price.py +5 -0
- solax_py_library/smart_scene/exceptions/smart_scene.py +81 -0
- solax_py_library/smart_scene/exceptions/weather.py +5 -0
- solax_py_library/smart_scene/types/__init__.py +0 -0
- solax_py_library/smart_scene/types/action.py +156 -0
- solax_py_library/smart_scene/types/condition.py +293 -0
- solax_py_library/smart_scene/types/smart_scene_content.py +173 -0
- solax_py_library/snap_shot/__init__.py +3 -3
- solax_py_library/snap_shot/constant/__init__.py +5 -5
- solax_py_library/snap_shot/constant/crc_table.py +258 -258
- solax_py_library/snap_shot/core/__init__.py +9 -9
- solax_py_library/snap_shot/core/base_modbus.py +14 -14
- solax_py_library/snap_shot/exceptions/__init__.py +3 -3
- solax_py_library/snap_shot/exceptions/snap_shot.py +9 -9
- solax_py_library/snap_shot/types/__init__.py +15 -15
- solax_py_library/snap_shot/types/address.py +39 -39
- solax_py_library/upload/__init__.py +3 -3
- solax_py_library/upload/api/__init__.py +3 -3
- solax_py_library/upload/api/service.py +24 -24
- solax_py_library/upload/core/__init__.py +3 -3
- solax_py_library/upload/core/data_adapter/__init__.py +5 -5
- solax_py_library/upload/core/data_adapter/base.py +9 -9
- solax_py_library/upload/core/data_adapter/csv.py +26 -26
- solax_py_library/upload/core/upload_service/__init__.py +15 -15
- solax_py_library/upload/core/upload_service/base.py +43 -43
- solax_py_library/upload/exceptions/__init__.py +8 -8
- solax_py_library/upload/exceptions/upload_error.py +21 -21
- solax_py_library/upload/test/test_ftp.py +113 -113
- solax_py_library/upload/types/__init__.py +11 -11
- solax_py_library/upload/types/client.py +19 -19
- solax_py_library/upload/types/ftp.py +37 -37
- solax_py_library/utils/cloud_client.py +213 -0
- solax_py_library/utils/common.py +38 -38
- solax_py_library/utils/struct_util.py +30 -30
- solax_py_library/utils/time_util.py +5 -0
- {solax_py_library-1.0.0.24.dist-info → solax_py_library-1.0.0.2501.dist-info}/METADATA +1 -1
- solax_py_library-1.0.0.2501.dist-info/RECORD +68 -0
- solax_py_library-1.0.0.24.dist-info/RECORD +0 -46
- {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,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"
|
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
|