|
10 | 10 |
|
11 | 11 | from custom_components.ecoflow_cloud.api import EcoflowApiClient |
12 | 12 | from custom_components.ecoflow_cloud.devices import BaseInternalDevice, const |
| 13 | +from custom_components.ecoflow_cloud.number import BatteryBackupLevel |
13 | 14 | from custom_components.ecoflow_cloud.sensor import ( |
14 | 15 | CapacitySensorEntity, |
15 | 16 | CumulativeCapacitySensorEntity, |
|
23 | 24 | TempSensorEntity, |
24 | 25 | WattsSensorEntity, |
25 | 26 | ) |
| 27 | +from custom_components.ecoflow_cloud.switch import EnabledEntity |
26 | 28 |
|
27 | 29 | _LOGGER = logging.getLogger(__name__) |
28 | 30 |
|
@@ -281,12 +283,107 @@ def sensors(self, client: EcoflowApiClient) -> list[SensorEntity]: |
281 | 283 | # "waterInFlag": 0, |
282 | 284 | ] |
283 | 285 |
|
| 286 | + def _build_proto_command(self, field_num: int, value: int) -> bytes: |
| 287 | + """Build a protobuf command payload for Stream AC internal API.""" |
| 288 | + import time |
| 289 | + from .proto import stream_ac_pb2 |
| 290 | + |
| 291 | + def encode_varint(n): |
| 292 | + result = bytearray() |
| 293 | + while True: |
| 294 | + bits = n & 0x7F |
| 295 | + n >>= 7 |
| 296 | + if n: |
| 297 | + result.append(0x80 | bits) |
| 298 | + else: |
| 299 | + result.append(bits) |
| 300 | + break |
| 301 | + return bytes(result) |
| 302 | + |
| 303 | + def encode_field(fnum, val): |
| 304 | + return encode_varint((fnum << 3) | 0) + encode_varint(val) |
| 305 | + |
| 306 | + pdata = encode_field(6, int(time.time())) + encode_field(field_num, value) |
| 307 | + |
| 308 | + import random |
| 309 | + header = stream_ac_pb2.StreamACHeader() |
| 310 | + header.src = 32 |
| 311 | + header.dest = 2 |
| 312 | + header.d_src = 1 |
| 313 | + header.d_dest = 1 |
| 314 | + header.cmd_func = 254 |
| 315 | + header.cmd_id = 17 |
| 316 | + header.data_len = len(pdata) |
| 317 | + header.need_ack = 1 |
| 318 | + header.seq = random.randint(100000000, 999999999) |
| 319 | + header.product_id = 58 |
| 320 | + header.version = 3 |
| 321 | + header.payload_ver = 1 |
| 322 | + setattr(header, "from", "Android") |
| 323 | + header.pdata = pdata |
| 324 | + |
| 325 | + msg = stream_ac_pb2.StreamACSendHeaderMsg() |
| 326 | + msg.msg.CopyFrom(header) |
| 327 | + return msg.SerializeToString() |
| 328 | + |
284 | 329 | # moduleWifiRssi |
285 | 330 | def numbers(self, client: EcoflowApiClient) -> list[NumberEntity]: |
286 | | - return [] |
| 331 | + return [ |
| 332 | + BatteryBackupLevel( |
| 333 | + client, |
| 334 | + self, |
| 335 | + "backupReverseSoc", |
| 336 | + const.BACKUP_RESERVE_LEVEL, |
| 337 | + 3, |
| 338 | + 95, |
| 339 | + "cmsMinDsgSoc", |
| 340 | + "cmsMaxChgSoc", |
| 341 | + 3, |
| 342 | + lambda value: { |
| 343 | + "sn": self.device_info.sn, |
| 344 | + "cmdId": 17, |
| 345 | + "cmdFunc": 254, |
| 346 | + "dirDest": 1, |
| 347 | + "dirSrc": 1, |
| 348 | + "dest": 2, |
| 349 | + "needAck": True, |
| 350 | + "params": { |
| 351 | + "cfgBackupReverseSoc": int(value), |
| 352 | + }, |
| 353 | + }, |
| 354 | + ), |
| 355 | + ] |
287 | 356 |
|
288 | 357 | def switches(self, client: EcoflowApiClient) -> list[SwitchEntity]: |
289 | | - return [] |
| 358 | + from custom_components.ecoflow_cloud.switch import EnabledEntity |
| 359 | + |
| 360 | + class ProtoEnabledEntity(EnabledEntity): |
| 361 | + def __init__(self_, *args, field_num, enable_val, disable_val, **kwargs): |
| 362 | + self_._field_num = field_num |
| 363 | + self_._enable_val = enable_val |
| 364 | + self_._disable_val = disable_val |
| 365 | + super().__init__(*args, **kwargs) |
| 366 | + |
| 367 | + def turn_on(self_, **kwargs): |
| 368 | + raw = self._build_proto_command(self_._field_num, self_._enable_val) |
| 369 | + client.mqtt_client.publish(self.device_info.set_topic, raw) |
| 370 | + |
| 371 | + def turn_off(self_, **kwargs): |
| 372 | + raw = self._build_proto_command(self_._field_num, self_._disable_val) |
| 373 | + client.mqtt_client.publish(self.device_info.set_topic, raw) |
| 374 | + |
| 375 | + return [ |
| 376 | + ProtoEnabledEntity( |
| 377 | + client, self, "feedGridMode", const.STREAM_FEED_IN_CONTROL, |
| 378 | + lambda value: {}, field_num=168, enable_val=1, disable_val=2, |
| 379 | + enableValue=2, disableValue=1, |
| 380 | + ), |
| 381 | + ProtoEnabledEntity( |
| 382 | + client, self, "relay2Onoff", const.MODE_AC1_ON, |
| 383 | + lambda value: {}, field_num=380, enable_val=1, disable_val=0, |
| 384 | + enableValue=True, disableValue=False, |
| 385 | + ), |
| 386 | + ] |
290 | 387 |
|
291 | 388 | def selects(self, client: EcoflowApiClient) -> list[SelectEntity]: |
292 | 389 | return [] |
|
0 commit comments