solax-py-library 1.0.0.2503__py3-none-any.whl → 1.0.0.2504__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.
@@ -1,9 +1,13 @@
1
1
  class BaseCondition(object):
2
- def __init__(self, **kwargs):
2
+ def __init__(self, update_value_function, **kwargs):
3
3
  self.value = {}
4
+ if callable(update_value_function):
5
+ raise ValueError("update_value_function must be callable")
6
+ self.update_value_function = update_value_function
4
7
 
5
8
  def update_value(self):
6
- ...
9
+ self.update_value_function()
10
+ print(self.value)
7
11
 
8
12
  def meet_func(self, data, ctx):
9
13
  ...
@@ -1,16 +1,16 @@
1
1
  from collections import defaultdict
2
2
 
3
3
  from solax_py_library.smart_scene.core.condition.base import BaseCondition
4
- from solax_py_library.smart_scene.types.condition import CabinetConditionItemData
4
+ from solax_py_library.smart_scene.types.condition import CabinetConditionItemData, CabinetConditionType
5
5
  from solax_py_library.device.types.alarm import AlarmLevel
6
6
 
7
7
 
8
8
  class CabinetCondition(BaseCondition):
9
- def __init__(self, **kwargs):
10
- super().__init__()
9
+ def __init__(self, update_value_function, **kwargs):
10
+ super().__init__(update_value_function, **kwargs)
11
11
  self.value = defaultdict(
12
12
  lambda: {
13
- "soc": None,
13
+ "soc": 0,
14
14
  "alarm_level": {
15
15
  AlarmLevel.EMERGENCY: False,
16
16
  AlarmLevel.NORMAL: False,
@@ -18,11 +18,21 @@ class CabinetCondition(BaseCondition):
18
18
  },
19
19
  }
20
20
  )
21
- self.redis_service = kwargs.get("redis_service")
22
- self.db_service = kwargs.get("db_service")
23
-
24
- def update_value(self):
25
- ...
26
21
 
27
22
  def meet_func(self, data: CabinetConditionItemData, ctx):
28
- ...
23
+ if not self.value:
24
+ return False
25
+ cabinet = ctx["cabinet"] or []
26
+ for cabinet_sn in cabinet:
27
+ if data.childType == CabinetConditionType.cabinetSoc:
28
+ if self.value[cabinet_sn]["soc"] is None:
29
+ return False
30
+ if data.childData.function.function()(
31
+ compare_value=self.value[cabinet_sn]["soc"],
32
+ base_value=data.childData.data[0],
33
+ ):
34
+ return True
35
+ elif data.childType == CabinetConditionType.cabinetAlarm:
36
+ if self.value[cabinet_sn]["alarm_level"][data.childData.data[0]]:
37
+ return True
38
+ return False
@@ -8,13 +8,10 @@ from solax_py_library.smart_scene.types.condition import (
8
8
 
9
9
 
10
10
  class DateCondition(BaseCondition):
11
- def __init__(self, **kwargs):
12
- super().__init__(**kwargs)
13
- now = datetime.now()
14
- self.hour = now.hour
15
- self.minute = now.minute
11
+ def __init__(self, update_value_function, **kwargs):
12
+ super().__init__(update_value_function, **kwargs)
16
13
 
17
- def update_value(self):
14
+ def _update_value(self):
18
15
  """获取time 类型需要的数据"""
19
16
  now = datetime.now()
20
17
  self.hour = now.hour
@@ -7,7 +7,6 @@ from solax_py_library.smart_scene.types.condition import (
7
7
  SmartSceneUnit,
8
8
  ConditionFunc,
9
9
  )
10
- from solax_py_library.utils.cloud_client import CloudClient
11
10
  from solax_py_library.utils.time_util import (
12
11
  trans_str_time_to_index,
13
12
  get_highest_or_lowest_value,
@@ -15,23 +14,10 @@ from solax_py_library.utils.time_util import (
15
14
 
16
15
 
17
16
  class ElePriceCondition(BaseCondition):
18
- def __init__(self, **kwargs):
19
- super().__init__(**kwargs)
17
+ def __init__(self, update_value_function, **kwargs):
18
+ super().__init__(update_value_function, **kwargs)
20
19
  self.buy = None
21
20
  self.unit = None
22
- self.cloud_url = kwargs.pop("cloud_url")
23
- self.sn = kwargs.pop("ems_sn")
24
- self.secret = kwargs.pop("secret")
25
- self.client = CloudClient(base_url=self.cloud_url)
26
-
27
- def update_value(self):
28
- """获取电价 类型需要的数据"""
29
- ele_info = self.client.get_electrovalence_data_from_cloud()
30
- if self.buy:
31
- self.value = ele_info.get("buy", [])
32
- else:
33
- self.value = ele_info.get("sell", [])
34
- self.unit = ele_info.get("ele_unit", " ")
35
21
 
36
22
  def meet_func_price(self, function_value: ConditionFunc, data_value, index) -> bool:
37
23
  """电价条件的判定"""
@@ -102,12 +88,12 @@ class ElePriceCondition(BaseCondition):
102
88
 
103
89
 
104
90
  class EleSellPriceCondition(ElePriceCondition):
105
- def __init__(self):
106
- super().__init__()
91
+ def __init__(self, update_value_function, **kwargs):
92
+ super().__init__(update_value_function, **kwargs)
107
93
  self.buy = False
108
94
 
109
95
 
110
96
  class ElsBuyPriceCondition(ElePriceCondition):
111
- def __init__(self):
112
- super().__init__()
97
+ def __init__(self, update_value_function, **kwargs):
98
+ super().__init__(update_value_function, **kwargs)
113
99
  self.buy = True
@@ -6,14 +6,10 @@ from solax_py_library.smart_scene.types.condition import (
6
6
 
7
7
 
8
8
  class SystemCondition(BaseCondition):
9
- def __init__(self, **kwargs):
10
- super().__init__(**kwargs)
11
- self.redis_service = kwargs.pop("redis_service")
12
-
13
- def update_value(self):
14
- overview_statistic_data = self.redis_service.get_overview_statistic_data()
15
- self.value["grid_active_power"] = overview_statistic_data[8]
16
- self.value["system_soc"] = overview_statistic_data[16]
9
+ def __init__(self, update_value_function, **kwargs):
10
+ super().__init__(update_value_function, **kwargs)
11
+ self.grid_active_power = None
12
+ self.system_soc = None
17
13
 
18
14
  def meet_system_condition(self, data: SystemConditionItemData, ctx):
19
15
  if not self.value:
@@ -22,15 +18,15 @@ class SystemCondition(BaseCondition):
22
18
  function_value = child_data.function
23
19
  compare_value = None
24
20
  if data.childType == SystemConditionType.systemExportPower:
25
- compare_value = self.value["grid_active_power"]
21
+ compare_value = self.grid_active_power
26
22
  if compare_value < 0:
27
23
  return False
28
24
  elif data.childType == SystemConditionType.systemImportPower:
29
- compare_value = self.value["grid_active_power"]
25
+ compare_value = self.grid_active_power
30
26
  if compare_value > 0:
31
27
  return False
32
28
  elif data.childType == SystemConditionType.systemSoc:
33
- compare_value = self.value["system_soc"]
29
+ compare_value = self.system_soc
34
30
  if compare_value is None:
35
31
  return False
36
32
  return function_value.function()(
@@ -3,20 +3,12 @@ from solax_py_library.smart_scene.types.condition import (
3
3
  WeatherConditionItemData,
4
4
  WeatherConditionType,
5
5
  )
6
- from solax_py_library.utils.cloud_client import CloudClient
7
6
  from solax_py_library.utils.time_util import get_rounded_times
8
7
 
9
8
 
10
9
  class WeatherCondition(BaseCondition):
11
- def __init__(self, **kwargs):
12
- super().__init__(**kwargs)
13
- self.cloud_url = kwargs.pop("cloud_url")
14
- self.sn = kwargs.pop("ems_sn")
15
- self.secret = kwargs.pop("secret")
16
- self.client = CloudClient(base_url=self.cloud_url)
17
-
18
- def update_value(self):
19
- self.value = self.client.get_weather_data_from_redis()
10
+ def __init__(self, update_value_function, **kwargs):
11
+ super().__init__(update_value_function, **kwargs)
20
12
 
21
13
  def meet_weather_condition(self, data: WeatherConditionItemData, ctx):
22
14
  if not self.value:
@@ -32,7 +24,7 @@ class WeatherCondition(BaseCondition):
32
24
  time_now = nearest_time
33
25
  index = self.value["timeList"].index(time_now)
34
26
  if child_type == WeatherConditionType.irradiance:
35
- return function_value.function()(function_value, data_value, index)
27
+ return self.meet_func_irradiance(function_value, data_value, index)
36
28
  elif child_type == WeatherConditionType.temperature:
37
29
  return function_value.function()(
38
30
  self.value[child_type]["valueList"][index],
File without changes
@@ -0,0 +1,411 @@
1
+ import json
2
+ import os.path
3
+ import struct
4
+ import traceback
5
+ import threading
6
+ import subprocess as sp
7
+
8
+ import requests
9
+ from datetime import datetime, timedelta
10
+
11
+ from tornado.log import app_log
12
+
13
+ from domain.ems_enum.smart_scene.condition import ConditionFunc
14
+ from settings.const import (
15
+ RedisKey,
16
+ Role,
17
+ DeviceInfo,
18
+ PCSOperateFunctionName,
19
+ Enumeration,
20
+ FilePath,
21
+ )
22
+ from web.web_ext import config_json
23
+ from models.tables import Device
24
+ from utils.common import options, get_logger_sn, md5_encrypt, read_write_json_file
25
+ from services.redis_service import RedisService
26
+ from models.sqlite_db import session_maker
27
+ from utils.redis_utils import RedisProducer
28
+ from services.strategy_control_service import StrategyControlService
29
+ from solax_py_library.utils.cloud_client import CloudClient
30
+
31
+ redis_host = options.REDIS_HOST
32
+ redis_port = options.REDIS_PORT
33
+ db = options.REDIS_DB
34
+ LOCAL_URL = "http://127.0.0.1:8000/ems/cloud/"
35
+
36
+
37
+ class SmartSceneService(object):
38
+ def __init__(self):
39
+ self.ems_sn = get_logger_sn()
40
+ self.redis_service = RedisService()
41
+ self.redis_producer = RedisProducer().producer
42
+ self.strategy_control_service = StrategyControlService()
43
+
44
+ def request_local_redis(self, redis_topic, payload):
45
+ """本地redis分发任务"""
46
+ try:
47
+ self.redis_producer.publish(redis_topic, payload)
48
+ except Exception:
49
+ app_log.info(traceback.format_exc())
50
+
51
+ def request_local_server(self, apiName, apiPostData, scene_id=None):
52
+ """
53
+ 访问本地接口, 调用server8000
54
+ 需要使用admin权限
55
+ :param apiName: 接口名称
56
+ :param apiPostData: 请求参数
57
+ :param scene_id: 请求标识
58
+ :return:
59
+ """
60
+ username = Role.SUPER_ADMIN
61
+ user_info = {"username": username, "password": md5_encrypt(self.ems_sn)}
62
+ token = self.redis_service.make_token(username, user_info)
63
+ url = LOCAL_URL + apiName
64
+ headers = {"token": token}
65
+ try:
66
+ response = requests.post(url, json=apiPostData, headers=headers, timeout=5)
67
+ app_log.info(
68
+ f"scene_id: {scene_id}, request apiName: {apiName}, response: {response.text}"
69
+ )
70
+ return json.loads(response.text)
71
+ except Exception:
72
+ app_log.info(traceback.format_exc())
73
+ return False
74
+
75
+ def get_token(self):
76
+ token = self.redis_service.get_token()
77
+ if not token:
78
+ client = CloudClient(base_url=config_json.get("CLOUD_URL", ""))
79
+ token = client.get_token(
80
+ ems_sn=self.ems_sn, sn_secret=config_json.get("MQTT_PASSWORD")
81
+ )
82
+ if not token:
83
+ app_log.info("访问token接口失败")
84
+ return False
85
+ self.redis_service.set_token(token)
86
+ return token
87
+ else:
88
+ return token
89
+
90
+ def get_weather_data_from_redis(self):
91
+ try:
92
+ weather_info = self.redis_service.get_ele_price_info() or {}
93
+ return weather_info
94
+ except Exception:
95
+ app_log.error(traceback.format_exc())
96
+ return {}
97
+
98
+ def get_weather_data_from_cloud(self):
99
+ """获取未来24小时天气数据"""
100
+ try:
101
+ token = self.get_token()
102
+ client = CloudClient(base_url=config_json.get("CLOUD_URL", ""))
103
+ weather_info = client.get_weather_data_from_cloud(
104
+ ems_sn=self.ems_sn,
105
+ token=token,
106
+ )
107
+ if not weather_info:
108
+ app_log.error(f"获取天气数据失败 异常 {traceback.format_exc()}")
109
+ return False
110
+ app_log.info(f"获取天气数据成功: {weather_info}")
111
+ self.redis_service.set_weather_info(weather_info)
112
+ except Exception:
113
+ app_log.error(f"获取天气数据失败 异常 {traceback.format_exc()}")
114
+ return False
115
+
116
+ def trans_str_time_to_index(self, now_time, minute=15):
117
+ """将时间按照minute切换为索引,时间格式为 %H-%M"""
118
+ time_list = [int(i) for i in now_time.split(":")]
119
+ time_int = time_list[0] * 4 + time_list[1] // minute
120
+ return time_int
121
+
122
+ def get_electrovalence_data_from_cloud(self):
123
+ try:
124
+ token = self.get_token()
125
+ client = CloudClient(base_url=config_json.get("CLOUD_URL", ""))
126
+ ele_price_info = client.get_electrovalence_data_from_cloud(
127
+ ems_sn=self.ems_sn, token=token
128
+ )
129
+ app_log.info(f"获取电价数据成功: {ele_price_info}")
130
+ self.redis_service.set_ele_price_info(ele_price_info)
131
+ except Exception:
132
+ app_log.error(f"获取电价数据失败 异常 {traceback.format_exc()}")
133
+ return False
134
+
135
+ def get_electrovalence_data_from_redis(self):
136
+ """从自定义电价的缓存中获取"""
137
+ month_temp = self.redis_service.hget(
138
+ RedisKey.MONTH_TEMPLATE, RedisKey.MONTH_TEMPLATE
139
+ )
140
+ month_temp = json.loads(month_temp) if month_temp else {}
141
+ if month_temp == {}:
142
+ return {}
143
+ month = datetime.strftime(datetime.now(), "%m")
144
+ month_map = {
145
+ "01": "Jan",
146
+ "02": "Feb",
147
+ "03": "Mar",
148
+ "04": "Apr",
149
+ "05": "May",
150
+ "06": "Jun",
151
+ "07": "Jul",
152
+ "08": "Aug",
153
+ "09": "Sep",
154
+ "10": "Oct",
155
+ "11": "Nov",
156
+ "12": "Dec",
157
+ }
158
+ month = month_map.get(month, None)
159
+ if month is None:
160
+ return {}
161
+ template_id = str(month_temp.get(month, 0))
162
+ price_info = self.redis_service.hget(
163
+ RedisKey.ELECTRICITY_PRICE_TEMPLATE, template_id
164
+ )
165
+ price_info = json.loads(price_info) if price_info else {}
166
+ if price_info == {}:
167
+ return {}
168
+
169
+ stationInfo = self.redis_service.get(RedisKey.STATION_INFO)
170
+ stationInfo = json.loads(stationInfo) if stationInfo else {}
171
+ currencyCode = stationInfo.get("currencyCode")
172
+ currency_file = read_write_json_file(FilePath.CURRENCY_PATH)
173
+ ele_unit = False
174
+ try:
175
+ for j in currency_file["list"]:
176
+ if currencyCode == j["code"]:
177
+ ele_unit = j["unit"].split(":")[-1] + "/kWh"
178
+ break
179
+ except:
180
+ app_log.error(f"获取本地电价单位出错 {traceback.format_exc()}")
181
+ ele_price_info = {
182
+ "buy": [None] * 192,
183
+ "sell": [None] * 192,
184
+ "date": datetime.strftime(datetime.now(), "%Y-%m-%d"),
185
+ "ele_unit": ele_unit if ele_unit else "/kWh",
186
+ }
187
+ for period in price_info["periodConfiguration"]:
188
+ start_index = self.trans_str_time_to_index(period["startTime"])
189
+ end_index = self.trans_str_time_to_index(period["endTime"])
190
+ slotName = period["slotName"]
191
+ for price in price_info["priceAllocation"]:
192
+ if price["slotName"] == slotName:
193
+ ele_price_info["buy"][start_index:end_index] = [
194
+ price["buyPrice"]
195
+ ] * (end_index - start_index)
196
+ ele_price_info["sell"][start_index:end_index] = [
197
+ price["salePrice"]
198
+ ] * (end_index - start_index)
199
+ break
200
+ return ele_price_info
201
+
202
+ def get_electrovalence_data(self):
203
+ try:
204
+ online_status = self.redis_service.hget(RedisKey.LED_STATUS_KEY, "blue")
205
+ online_status = (
206
+ json.loads(online_status).get("status") if online_status else 0
207
+ )
208
+ if online_status:
209
+ ele_price_info = self.redis_service.get_ele_price_info() or {}
210
+ today = datetime.strftime(datetime.now(), "%Y-%m-%d")
211
+ date_now = ele_price_info.get("date", "")
212
+ if today not in date_now:
213
+ ele_price_info = {}
214
+ else:
215
+ app_log.info("设备离线,获取本地电价")
216
+ ele_price_info = self.get_electrovalence_data_from_redis()
217
+ return ele_price_info
218
+ except Exception:
219
+ app_log.error(traceback.format_exc())
220
+ return {}
221
+
222
+ def get_highest_or_lowest_price(
223
+ self, start_time, end_time, hours, price_list, func="expensive_hours"
224
+ ):
225
+ """获取一段时间内,电价最高或最低的几个小时"""
226
+ start_index = self.trans_str_time_to_index(start_time)
227
+ end_index = self.trans_str_time_to_index(end_time)
228
+ arr = price_list[start_index:end_index]
229
+ if None in arr:
230
+ return False
231
+ indices = list(range(end_index - start_index))
232
+ if func == "expensive_hours":
233
+ reverse = True
234
+ else:
235
+ reverse = False
236
+ sorted_indices = sorted(indices, key=lambda i: arr[i], reverse=reverse)
237
+ return sorted_indices[: int(hours * 4)], start_index
238
+
239
+ def get_rounded_times(self):
240
+ """
241
+ 返回距离当前时间最近的15min的整点时间以及后一整点5min时间(天气是预测未来15min的,也就是在00:00时,只能拿到00:15的数据)
242
+ """
243
+ now = datetime.now()
244
+ # 确定当前时间所属的15分钟区间
245
+ index_1 = now.minute // 15
246
+ index_2 = now.minute % 15
247
+ left_time = now.replace(minute=15 * index_1, second=0, microsecond=0)
248
+ right_time = left_time + timedelta(minutes=15)
249
+ if index_2 < 8:
250
+ nearest_time = left_time
251
+ else:
252
+ nearest_time = right_time
253
+ return datetime.strftime(nearest_time, "%Y-%m-%d %H:%M:%S"), datetime.strftime(
254
+ right_time, "%Y-%m-%d %H:%M:%S"
255
+ )
256
+
257
+ def compare_the_magnitudes(self, function, compare_value, base_value):
258
+ """比较两个值"""
259
+ if function == ConditionFunc.GT:
260
+ return compare_value > base_value
261
+ elif function == ConditionFunc.EQ:
262
+ return compare_value == base_value
263
+ elif function == ConditionFunc.LT:
264
+ return compare_value < base_value
265
+ return False
266
+
267
+ def get_cabinet_type(self):
268
+ # 获取机柜类型
269
+ cabinet_type = 1
270
+ with session_maker(change=False) as session:
271
+ result = (
272
+ session.query(Device.deviceModel)
273
+ .filter(Device.deviceType == DeviceInfo.ESS_TYPE, Device.isDelete == 0)
274
+ .first()
275
+ )
276
+
277
+ if result:
278
+ cabinet_type = result[0]
279
+ if cabinet_type in [3, 4, 7, 8]:
280
+ key_name = "AELIO"
281
+ else:
282
+ key_name = "TRENE"
283
+ return key_name
284
+
285
+ def set_manual_mode(self, work_mode, power, soc, cabinet_type):
286
+ """设置手动模式"""
287
+ # 充电
288
+ if work_mode == 3:
289
+ strategy_info = {
290
+ "chargePower": power,
291
+ "chargeTargetSoc": soc,
292
+ "runState": 1,
293
+ }
294
+ self.redis_service.hset(
295
+ RedisKey.MANUAL_STRATEGY_INFO,
296
+ RedisKey.CHARGE_MODE,
297
+ json.dumps(strategy_info),
298
+ )
299
+ self.strategy_control_service.apply_or_stop_strategy(
300
+ 0, RedisKey.MANUAL_STRATEGY_INFO, RedisKey.DISCHARGE_MODE
301
+ )
302
+ # 将运行模式修改为手动
303
+ self.redis_service.set(RedisKey.RUN_STRATEGY_TYPE, 1)
304
+ elif work_mode == 4:
305
+ strategy_info = {
306
+ "dischargePower": power,
307
+ "dischargeTargetSoc": soc,
308
+ "runState": 1,
309
+ }
310
+ self.redis_service.hset(
311
+ RedisKey.MANUAL_STRATEGY_INFO,
312
+ RedisKey.DISCHARGE_MODE,
313
+ json.dumps(strategy_info),
314
+ )
315
+ # 将充电状态修改
316
+ self.strategy_control_service.apply_or_stop_strategy(
317
+ 0, RedisKey.MANUAL_STRATEGY_INFO, RedisKey.CHARGE_MODE
318
+ )
319
+ # 将运行模式修改为手动
320
+ self.redis_service.set(RedisKey.RUN_STRATEGY_TYPE, 1)
321
+ # 停止
322
+ else:
323
+ self.strategy_control_service.apply_or_stop_strategy(
324
+ 0, RedisKey.MANUAL_STRATEGY_INFO, RedisKey.DISCHARGE_MODE
325
+ )
326
+ self.strategy_control_service.apply_or_stop_strategy(
327
+ 0, RedisKey.MANUAL_STRATEGY_INFO, RedisKey.CHARGE_MODE
328
+ )
329
+ self.redis_service.set(RedisKey.RUN_STRATEGY_TYPE, 1)
330
+ if work_mode == 3:
331
+ manualType = 1
332
+ elif work_mode == 4:
333
+ manualType = 2
334
+ else:
335
+ manualType = 3
336
+ data = {
337
+ "power": power,
338
+ "soc": soc,
339
+ "workMode": work_mode,
340
+ "manualType": manualType,
341
+ "useMode": 3,
342
+ }
343
+ if cabinet_type in ["TRENE"]:
344
+ t = threading.Thread(
345
+ target=self.request_local_redis, args=(options.REDIS_POWER_CONTROL, "")
346
+ )
347
+ else:
348
+ func_name = PCSOperateFunctionName.SET_AELIO_USE_MODE
349
+ channel = options.REDIS_WRITE_SERIAL_DEVICE
350
+ future_data = {}
351
+ future_data["func_name"] = func_name
352
+ future_data["operationMode"] = Enumeration.SINGLE_DEVICE_MODE
353
+ future_data["data"] = data
354
+ t = threading.Thread(
355
+ target=self.request_local_redis,
356
+ args=(channel, json.dumps(future_data)),
357
+ daemon=True,
358
+ )
359
+ t.start()
360
+ return True
361
+
362
+ def struct_transform(self, value, fmt, order="big"):
363
+ """将10进制的原始值转换为modbus协议需要的精度与类型的值"""
364
+ value = int(value)
365
+ if order == "little":
366
+ opt = "<"
367
+ else:
368
+ opt = ">"
369
+ try:
370
+ if fmt == "int16":
371
+ ret = struct.pack(f"{opt}h", value)
372
+ ret_list = struct.unpack(f"{opt}H", ret)
373
+ elif fmt == "uint16":
374
+ ret = struct.pack(f"{opt}H", value)
375
+ ret_list = struct.unpack(f"{opt}H", ret)
376
+ elif fmt == "int32":
377
+ ret = struct.pack(f"{opt}i", value)
378
+ ret_list = struct.unpack(f"{opt}HH", ret)
379
+ elif fmt == "uint32":
380
+ ret = struct.pack(f"{opt}I", value)
381
+ ret_list = struct.unpack(f"{opt}HH", ret)
382
+ elif fmt == "int64":
383
+ ret = struct.pack(f"{opt}q", value)
384
+ ret_list = struct.unpack(f"{opt}HHHH", ret)
385
+ elif fmt == "uint64":
386
+ ret = struct.pack(f"{opt}Q", value)
387
+ ret_list = struct.unpack(f"{opt}HHHH", ret)
388
+ else:
389
+ ret_list = [0]
390
+ except Exception:
391
+ if "16" in fmt:
392
+ ret_list = [0]
393
+ elif "32" in fmt:
394
+ ret_list = [0, 0]
395
+ else:
396
+ ret_list = [0, 0, 0, 0]
397
+ return list(ret_list)
398
+
399
+ def ems_do_control(self, data):
400
+ for do_info in data:
401
+ do_number = do_info.DoNumber
402
+ do_value = do_info.DoValue
403
+ if 1 <= do_number <= 8 and do_value in [0, 1]:
404
+ path = DeviceInfo.DI_DO_GPIO_MAPPING[f"DO{do_number}"] + "/value"
405
+ if not os.path.exists(path):
406
+ app_log.info(f"DO path not exists {do_info}")
407
+ else:
408
+ cmd = f"echo {do_value} > {path}"
409
+ ret = sp.getstatusoutput(cmd)
410
+ ret = True if ret[0] == 0 else False
411
+ app_log.info(f"DO {do_info} 控制结果: {ret}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: solax-py-library
3
- Version: 1.0.0.2503
3
+ Version: 1.0.0.2504
4
4
  Summary: some common tool
5
5
  Author: shenlvyu
6
6
  Author-email: 13296718439@163.com
@@ -20,12 +20,14 @@ solax_py_library/smart_scene/core/action/base.py,sha256=vSC3rL9qfPy41Nlds_0QcTLM
20
20
  solax_py_library/smart_scene/core/action/ems_action.py,sha256=tasL7pUJbnmrEEokAa9Mug5eUFvHuFKGYZKNvNMdrBs,610
21
21
  solax_py_library/smart_scene/core/action/system_action.py,sha256=mVLTg9pZ7tDayLbr3I92a33snA2AGT3meUdm_XYDQEc,10839
22
22
  solax_py_library/smart_scene/core/condition/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- solax_py_library/smart_scene/core/condition/base.py,sha256=dQK5gRguOo7GmQkRSnae2kTv4wvZh7zJeyQCdX09OmE,177
24
- solax_py_library/smart_scene/core/condition/cabinet_condition.py,sha256=vbo5wHf_p1qoTgItcyh-DcxE4GOe-2mUK5tYCg5pRCM,879
25
- solax_py_library/smart_scene/core/condition/date_condition.py,sha256=NXDN2A7t6h8r_F9GNUD8YPAJLFedFDXlrQhs5vvyNyo,899
26
- solax_py_library/smart_scene/core/condition/price_condition.py,sha256=4k4oDKSiSFdAM5reRVdfBW25XNw0o1QKYm0PUB_Mnxw,4174
27
- solax_py_library/smart_scene/core/condition/system_condition.py,sha256=100lBBf_yhdPbzK000U1OSfanKtA2xH4PoM6RRc42x0,1522
28
- solax_py_library/smart_scene/core/condition/weather_condition.py,sha256=kQ-0pM07G9-mOqOGp6BNbEdP3HuZyOdBqTQD0cAl8Ck,2469
23
+ solax_py_library/smart_scene/core/condition/base.py,sha256=T3R18uYh-XLXOXdtn_WwHS4nBoakJomHWLLAjs-0K1I,425
24
+ solax_py_library/smart_scene/core/condition/cabinet_condition.py,sha256=HbiIrOHzlGowEww8JD8ioScmayhtMNiVS8YzOHwoElw,1505
25
+ solax_py_library/smart_scene/core/condition/date_condition.py,sha256=8dbYhftUzGKQJkDqIkV_hjf4PIhKJrUCo9fv-_93sl8,855
26
+ solax_py_library/smart_scene/core/condition/price_condition.py,sha256=kAIkg5VxTOd5gTRHzqU23ZgFYSkcvF_haqzVKA-uDZc,3768
27
+ solax_py_library/smart_scene/core/condition/system_condition.py,sha256=Pstq_y6fdwh7a39q8JZuabymFeJx5AH5Xjj4MZz4or0,1309
28
+ solax_py_library/smart_scene/core/condition/weather_condition.py,sha256=jn8gbQ2ITvwNIlimib7zv-j5BvslFfJebJoftCGECeE,2173
29
+ solax_py_library/smart_scene/core/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ solax_py_library/smart_scene/core/service/smart_scene_service.py,sha256=wbC4Y0NK7vQRgJXrHCptjlb7r_C5PHLEHyGU1oaFNjw,15704
29
31
  solax_py_library/smart_scene/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
32
  solax_py_library/smart_scene/exceptions/price.py,sha256=ztsM8leCsxbzx1TYS5GQAVbfvxlU49QymS1NUykqM_g,142
31
33
  solax_py_library/smart_scene/exceptions/smart_scene.py,sha256=VFjRe7fUg5JA13Wg8BFvxzlF-pBSNaGJJBpnphYdSbM,1774
@@ -71,6 +73,6 @@ solax_py_library/utils/cloud_client.py,sha256=5dZrc5fzrNFSXqTPZd7oHt-Y9Jj6RCigB7
71
73
  solax_py_library/utils/common.py,sha256=bfnZcX9uM-PjJrYAFv1UMmZgt6bGR7MaOd7jRPNHGxw,1238
72
74
  solax_py_library/utils/struct_util.py,sha256=4SKx5IyAke88PGHPHDK3OEDtyGHdapGoQ1BnGR0F2ts,913
73
75
  solax_py_library/utils/time_util.py,sha256=bY5kj9dmyOuLEQ6uYGQK7jU7y1RMiHZgevEKnkcQcSU,1461
74
- solax_py_library-1.0.0.2503.dist-info/METADATA,sha256=vk7Hfoh4mLBm6qpGLhpsqNgPN01FRrex8Cu24KqzNYg,1827
75
- solax_py_library-1.0.0.2503.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
76
- solax_py_library-1.0.0.2503.dist-info/RECORD,,
76
+ solax_py_library-1.0.0.2504.dist-info/METADATA,sha256=4Z1yv8A7yWqkA0cCMP7UQG88cvcAMx33shtI-Huk5CU,1827
77
+ solax_py_library-1.0.0.2504.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
78
+ solax_py_library-1.0.0.2504.dist-info/RECORD,,