Skip to content

Commit

Permalink
v1.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
lunDreame authored Aug 16, 2024
1 parent 5b065dc commit 5fd3bcd
Show file tree
Hide file tree
Showing 14 changed files with 106 additions and 90 deletions.
70 changes: 42 additions & 28 deletions custom_components/bestin/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,25 @@
class BestinAPIv2:
"""Bestin HDC Smarthome API v2 Class."""

def __init__(self) -> None:
def __init__(self, hass, entry) -> None:
"""API initialization."""
self.elevator_count = entry.data.get("elevator_count", 1)
self.elevator_arrived = False

self.features_list: list = []
self.elev_moveinfo: dict = {}
self.elevator_data: dict = {}

def setup_elevators(self):
for count in range(1, self.elevator_count + 1):
self.setup_device("elevator", 1, str(count), False)
self.setup_device("elevator", 1, f"floor_{str(count)}", "대기 층")
self.setup_device("elevator", 1, f"direction_{str(count)}", "대기")

async def _v2_device_status(self, args=None):
if args is not None:
LOGGER.debug(f"Task execution started with argument: {args}")
self.last_update_time = args

if self.features_list:
await self.process_features(self.features_list)
else:
Expand Down Expand Up @@ -107,6 +114,8 @@ async def elevator_call_request(self) -> None:

if response.status == 200 and result_status == "ok":
LOGGER.info(f"Just a central server elevator request successful")
for count in range(1, self.elevator_count + 1):
self.setup_device("elevator", 1, str(count), False)
self.hass.create_task(self.fetch_elevator_status())
else:
LOGGER.error(f"Only central server elevator request failed: {response_data}")
Expand Down Expand Up @@ -139,28 +148,26 @@ async def handle_message_info(self, message) -> None:
data = json.loads(message)

if "move_info" in data:
if not os.path.exists('data.json'):
async with aiofiles.open('data.json', 'w') as file:
await file.write(json.dumps(data["move_info"], indent=4))

self.elev_moveinfo.update(data["move_info"])
#LOGGER.debug(f"Elevator move updated: {self.elev_moveinfo}")
serial = data["move_info"]["Serial"]
move_info = data["move_info"]

self.elevator_data = {serial: move_info}
self.elevator_data = dict(sorted(self.elevator_data.items()))
LOGGER.debug(f"Elevator data: {self.elevator_data}")
if len(self.elevator_data) >= 2:
for idx, (serial, info) in enumerate(self.elevator_data.items(), start=1):
floor = info["move_info"]["Floor"]
move_dir = info["move_info"]["MoveDir"]

serial = str(self.elev_moveinfo.get("Serial", "1"))
floor = f"{str(self.elev_moveinfo.get('Floor', '대기'))} 층"
movedir = self.elev_moveinfo.get("MoveDir", "대기")

self.setup_device("elevator", 1, f"floor_{serial}", floor)
self.setup_device("elevator", 1, f"direction_{serial}", movedir)
self.setup_device("elevator", 1, f"floor_{str(idx)}", floor)
self.setup_device("elevator", 1, f"direction_{str(idx)}", move_dir)
else:
self.setup_device("elevator", 1, f"floor_1", move_info["Floor"])
self.setup_device("elevator", 1, f"direction_1", move_info["MoveDir"])
else:
self.elev_moveinfo = data

serial = str(self.elev_moveinfo.get("Serial", "1"))
floor = f"{str(self.elev_moveinfo.get('Floor', '도착'))} 층"

self.setup_device("elevator", 1, serial, False)
self.setup_device("elevator", 1, f"floor_{serial}", floor)
self.setup_device("elevator", 1, f"direction_{serial}", "도착")
for count in range(1, self.elevator_count + 1):
self.setup_device("elevator", 1, f"floor_{str(count)}", "도착 층")
self.setup_device("elevator", 1, f"direction_{str(count)}", "도착")
self.elevator_arrived = True

async def request_feature_command(self, device_type: str, room_id: int, unit: str, value: str) -> None:
Expand Down Expand Up @@ -273,16 +280,18 @@ def __init__(
hub_id: str,
version: str,
version_identifier: str,
elevator_registration: bool,
async_add_device: Callable
) -> None:
"""API initialization."""
super().__init__()
super().__init__(hass, entry)
self.hass = hass
self.entry = entry
self.entities = entities
self.hub_id = hub_id
self.version = version
self.version_identifier = version_identifier
self.elevator_registration = elevator_registration
self.async_add_device = async_add_device

ssl_context = ssl.create_default_context()
Expand All @@ -302,14 +311,17 @@ def __init__(
def get_short_hash(self, id: str) -> str:
"""Generate a short hash for a given id."""
hash_object = hashlib.sha256(id.encode()).digest()
return base64.urlsafe_b64encode(hash_object)[:8].decode("utf-8")
return base64.urlsafe_b64encode(hash_object)[:8].decode("utf-8").upper()

async def start(self) -> None:
"""Start main loop with asyncio."""
self.stop_event.clear()
self.tasks.append(asyncio.create_task(self.schedule_session_refresh()))
await asyncio.sleep(1)

if self.elevator_registration:
LOGGER.debug("Setting up elevator")
self.setup_elevators()
v_key = getattr(self, f"_v{self.version[7:8]}_device_status")
scan_interval = self.entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
interval = timedelta(minutes=scan_interval)
Expand Down Expand Up @@ -445,15 +457,17 @@ def initialize_device(self, device_id: str, unit_id: Optional[str], state: Any)

if full_unique_id not in self.devices:
device_info = DeviceInfo(
id=full_unique_id,
type=device_type,
unique_id=full_unique_id,
device_type=device_type,
name=unique_id,
room=device_room,
state=state,
sub_type=unit_id or "",
colon_id=device_type if ":" in device_type else ""
)
device = Device(
info=device_info,
platform=platform,
domain=platform,
on_command=self.on_command,
callbacks=set()
)
Expand Down
2 changes: 1 addition & 1 deletion custom_components/bestin/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def async_add_climate(devices=None):
entities = [
BestinClimate(device, hub)
for device in devices
if device.info.id not in hub.entities[DOMAIN]
if device.info.unique_id not in hub.entities[DOMAIN]
]

if entities:
Expand Down
2 changes: 2 additions & 0 deletions custom_components/bestin/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
DOMAIN,
LOGGER,
DEFAULT_PORT,
DEFAULT_ELEVATOR_COUNT,
DEFAULT_SCAN_INTERVAL,
DEFAULT_MAX_TRANSMISSION,
DEFAULT_PACKET_VIEWER,
Expand Down Expand Up @@ -237,6 +238,7 @@ async def async_step_center_v2(

data_schema = vol.Schema({
vol.Optional(CONF_IP_ADDRESS): cv.string,
vol.Required("elevator_count", default=DEFAULT_ELEVATOR_COUNT): ConfigFlow.int_between(1, 3),
vol.Required(CONF_UUID): cv.string,
})

Expand Down
19 changes: 12 additions & 7 deletions custom_components/bestin/const.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

from typing import Any, Callable
from dataclasses import dataclass
from typing import Callable, Any, Optional, Set
from dataclasses import dataclass, field

from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.const import (
Expand All @@ -14,7 +14,7 @@

DOMAIN = "bestin"
NAME = "BESTIN"
VERSION = "1.1.0"
VERSION = "1.1.1"

PLATFORMS: list[Platform] = [
Platform.CLIMATE,
Expand All @@ -27,6 +27,7 @@
LOGGER: logging.Logger = logging.getLogger(__package__)

DEFAULT_PORT: int = 8899
DEFAULT_ELEVATOR_COUNT: int = 1
DEFAULT_SCAN_INTERVAL: int = 15
DEFAULT_MAX_TRANSMISSION: int = 10

Expand Down Expand Up @@ -175,22 +176,26 @@
@dataclass
class DeviceInfo:
"""Represents the basic information of a device."""
id: str
type: str
unique_id: str
device_type: str
name: str
room: str
state: Any
colon_id: Optional[str] = None
sub_type: Optional[str] = None

@dataclass
class Device:
"""Represents a device with callbacks and update functionalities."""
info: DeviceInfo
platform: Platform
domain: str
on_command: Callable
callbacks: set[Callable]
callbacks: Set[Callable] = field(default_factory=set)

def add_callback(self, callback: Callable):
"""Add a callback to the set of callbacks."""
self.callbacks.add(callback)

def remove_callback(self, callback: Callable):
"""Remove a callback from the set of callbacks, if it exists."""
self.callbacks.discard(callback)
8 changes: 5 additions & 3 deletions custom_components/bestin/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,15 +305,17 @@ def initialize_device(self, device_id: str, sub_id: Optional[str], state: Any) -

if full_unique_id not in self.devices:
device_info = DeviceInfo(
id=full_unique_id,
type=device_type,
unique_id=full_unique_id,
device_type=device_type,
name=unique_id,
room=device_room,
state=state,
sub_type=sub_id or "",
colon_id=device_type if ":" in device_type else ""
)
device = Device(
info=device_info,
platform=platform,
domain=platform,
on_command=self.on_command,
callbacks=set()
)
Expand Down
58 changes: 31 additions & 27 deletions custom_components/bestin/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,12 @@

from typing import Any

from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.entity import Entity, DeviceInfo
from homeassistant.core import callback

from .const import DOMAIN, MAIN_DEVICES


def split_dt(dt: str) -> str:
"""
Split the first part by a colon,
if there is no colon, return the entire string.
"""
return dt.split(":")[0].title() if ":" in dt else dt.title()


class BestinBase:
"""Base class for BESTIN devices."""

Expand All @@ -29,32 +21,44 @@ def __init__(self, device, hub):
@property
def unique_id(self) -> str:
"""Return a unique ID."""
return self._device.info.id
return self._device.info.unique_id

@property
def device_info(self):
"""Return device registry information for this entity."""
base_info = {
"connections": {(self.hub.hub_id, self.unique_id)},
"identifiers": {(DOMAIN, f"{self.hub.wp_version}_{split_dt(self._device.info.type)}")},
"manufacturer": "HDC Labs Co., Ltd.",
"model": self.hub.wp_version,
"name": f"{self.hub.name} {split_dt(self._device.info.type)}",
"sw_version": self.hub.sw_version,
"via_device": (DOMAIN, self.hub.hub_id),
}
if self._device.info.type in MAIN_DEVICES:
base_info["identifiers"] = {(DOMAIN, f"{self.hub.wp_version}_{self.hub.model}")}
base_info["name"] = f"{self.hub.name}"
def device_type_name(self) -> str:
"""Returns the formatted device type name."""
device_type = self._device.info.device_type
return (device_type.split(":")[0].title()
if ":" in device_type else device_type.title())

return base_info
@property
def device_info(self) -> DeviceInfo:
"""Return device registry information for this entity."""
if self._device.info.device_type in MAIN_DEVICES:
return DeviceInfo(
connections={(self.hub.hub_id, self.unique_id)},
identifiers={(DOMAIN, f"{self.hub.wp_version}_{self.hub.model}")},
manufacturer="HDC Labs Co., Ltd.",
model=self.hub.wp_version,
name=self.hub.name,
sw_version=self.hub.sw_version,
via_device=(DOMAIN, self.hub.hub_id),
)
return DeviceInfo(
connections={(self.hub.hub_id, self.unique_id)},
identifiers={(DOMAIN, f"{self.hub.wp_version}_{self.device_type_name}")},
manufacturer="HDC Labs Co., Ltd.",
model=self.hub.wp_version,
name=f"{self.hub.name} {self.device_type_name}",
sw_version=self.hub.sw_version,
via_device=(DOMAIN, self.hub.hub_id),
)

async def _on_command(self, data: Any = None, **kwargs):
"""Send commands to the device."""
await self._device.on_command(self.unique_id, data, **kwargs)


class BestinDevice(BestinBase, RestoreEntity):
class BestinDevice(BestinBase, Entity):
"""Define the Bestin Device entity."""

TYPE = ""
Expand Down Expand Up @@ -109,7 +113,7 @@ def extra_state_attributes(self) -> dict:
attributes = {
"unique_id": self.unique_id,
"device_room": self._device.info.room,
"device_type": self._device.info.type,
"device_type": self._device.info.device_type,
}
if self.should_poll:
attributes["last_update_time"] = self.hub.api.last_update_time
Expand Down
2 changes: 1 addition & 1 deletion custom_components/bestin/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def async_add_fan(devices=None):
entities = [
BestinFan(device, hub)
for device in devices
if device.info.id not in hub.entities[DOMAIN]
if device.info.unique_id not in hub.entities[DOMAIN]
]

if entities:
Expand Down
Loading

0 comments on commit 5fd3bcd

Please sign in to comment.