Skip to content

Commit

Permalink
Refactor loading data and new data:
Browse files Browse the repository at this point in the history
- use multicommand
- load hashboard data (temp and hashrate)
- use same hashrate as braiins UI
  • Loading branch information
Schnitzel committed May 29, 2022
1 parent d5b89cb commit ad108dd
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 110 deletions.
64 changes: 46 additions & 18 deletions custom_components/miner/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,46 +56,74 @@ async def _async_update_data(self):
"""Fetch sensors from miners."""

miner_ip = ipaddress.ip_address(self.entry.data[CONF_IP])
miner_data = {}
miner_api_data = {}
model = {}

try:
if self.miner is None:
self.miner = await self.miner_factory.get_miner(miner_ip)
miner_data = await self.miner.get_data()
# miner_version = await self.miner.get_version()
tunerstatus = await self.miner.api.tunerstatus()
miner_api_data = await self.miner.api.multicommand(
"summary", "temps", "tunerstatus", "devs", "fans"
)
model = await self.miner.get_model()

except APIError as err:
raise UpdateFailed("API Error") from err

data = {}
data["hostname"] = self.entry.data[CONF_HOSTNAME]
data["model"] = miner_data["Model"]
data["ip"] = miner_data["IP"]
data["model"] = model
data["ip"] = self.miner.ip
# data["version"] = miner_version
data["sensors"] = {}
data["sensors"]["temperature"] = miner_data["Temperature"]
data["sensors"]["hashrate"] = miner_data["Hashrate"]

data["number"] = {}
tuner = tunerstatus.get("TUNERSTATUS")
data["miner_sensors"] = {}

summary = miner_api_data.get("summary")[0].get("SUMMARY")
hashrate = summary[0].get("MHS 1m")
if hashrate:
data["miner_sensors"]["hashrate"] = round(hashrate / 1000000, 2)

temps = miner_api_data.get("temps")[0].get("TEMPS")
data["board_sensors"] = {}
chip_temps = [0]
if temps:
for temp_hashboard in temps:
data["board_sensors"][temp_hashboard["ID"]] = {}
data["board_sensors"][temp_hashboard["ID"]][
"board_temperature"
] = temp_hashboard["Board"]
data["board_sensors"][temp_hashboard["ID"]][
"chip_temperature"
] = temp_hashboard["Chip"]
chip_temps.append(temp_hashboard["Chip"])

data["miner_sensors"]["temperature"] = max(chip_temps)

tuner = miner_api_data.get("tunerstatus")[0].get("TUNERSTATUS")
if tuner:
if len(tuner) > 0:
power_limit = tuner[0].get("PowerLimit")
if power_limit:
data["number"]["power_limit"] = power_limit
data["miner_sensors"]["power_limit"] = power_limit
miner_consumption = tuner[0].get("ApproximateMinerPowerConsumption")
if miner_consumption:
data["sensors"]["miner_consumption"] = miner_consumption
data["miner_sensors"]["miner_consumption"] = miner_consumption
else:
data["sensors"]["miner_consumption"] = None
data["miner_sensors"]["miner_consumption"] = None
dynamic_power_scaling = tuner[0].get("DynamicPowerScaling")
if dynamic_power_scaling == "InitialPowerLimit":
data["sensors"]["scaled_power_limit"] = power_limit
data["miner_sensors"]["scaled_power_limit"] = power_limit
else:
scaled_power_limit = dynamic_power_scaling.get("ScaledPowerLimit")
if scaled_power_limit:
data["sensors"]["scaled_power_limit"] = scaled_power_limit
data["miner_sensors"]["scaled_power_limit"] = scaled_power_limit
else:
data["sensors"]["scaled_power_limit"] = None
data["miner_sensors"]["scaled_power_limit"] = None

devs = miner_api_data.get("devs")[0].get("DEVS")
if devs:
for hashboard in devs:
data["board_sensors"][hashboard["ID"]]["board_hashrate"] = round(
hashboard["MHS 1m"] / 1000000, 2
)

return data
6 changes: 3 additions & 3 deletions custom_components/miner/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def __init__(
super().__init__(coordinator=coordinator)
self._attr_unique_id = f"{self.coordinator.data['hostname']}-power_limit"

self._attr_value = self.coordinator.data["number"]["power_limit"]
self._attr_value = self.coordinator.data["miner_sensors"]["power_limit"]

self._attr_min_value = 100
self._attr_max_value = 5000
Expand All @@ -91,7 +91,7 @@ def __init__(
@property
def name(self) -> str | None:
"""Return name of the entity."""
return f"{self.coordinator.data['hostname']} PowerLimit"
return f"{self.coordinator.data['hostname']} Power Limit"

@property
def device_info(self) -> entity.DeviceInfo:
Expand Down Expand Up @@ -119,6 +119,6 @@ async def async_set_value(self, value):
@callback
def _handle_coordinator_update(self) -> None:

self._attr_value = self.coordinator.data["number"]["power_limit"]
self._attr_value = self.coordinator.data["miner_sensors"]["power_limit"]

super()._handle_coordinator_update()
199 changes: 112 additions & 87 deletions custom_components/miner/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,29 @@ class MinerNumberEntityDescription(SensorEntityDescription):
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE,
),
"board_temperature": MinerSensorEntityDescription(
"Board Temperature",
native_unit_of_measurement=TEMP_CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE,
),
"chip_temperature": MinerSensorEntityDescription(
"Chip Temperature",
native_unit_of_measurement=TEMP_CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE,
),
"hashrate": MinerSensorEntityDescription(
"Hashrate",
native_unit_of_measurement="TH/s",
state_class=SensorStateClass.MEASUREMENT,
device_class="Hashrate",
),
"board_hashrate": MinerSensorEntityDescription(
"Board Hashrate",
native_unit_of_measurement="TH/s",
state_class=SensorStateClass.MEASUREMENT,
device_class="Hashrate",
),
"power_limit": MinerSensorEntityDescription(
"Power Limit",
Expand Down Expand Up @@ -90,33 +110,65 @@ async def async_setup_entry(
) -> None:
"""Add sensors for passed config_entry in HA."""
coordinator: MinerCoordinator = hass.data[DOMAIN][config_entry.entry_id]
created = set()
sensor_created = set()

@callback
def _create_entity(key: str) -> MinerSensor:
"""Create a sensor entity."""
created.add(key)
def _create_miner_entity(key: str) -> MinerSensor:
"""Create a miner sensor entity."""
sensor_created.add(key)
description = ENTITY_DESCRIPTION_KEY_MAP.get(
key, MinerSensorEntityDescription("base_sensor")
)
return MinerSensor(
coordinator=coordinator, key=key, entity_description=description
)

@callback
def _create_board_entity(board: str, sensor: str) -> MinerBoardSensor:
"""Create a board sensor entity."""
sensor_created.add(f"{board}-{sensor}")
description = ENTITY_DESCRIPTION_KEY_MAP.get(
sensor, MinerSensorEntityDescription("base_sensor")
)
return MinerBoardSensor(
coordinator=coordinator,
board=board,
sensor=sensor,
entity_description=description,
)

await coordinator.async_config_entry_first_refresh()
async_add_entities(_create_entity(key) for key in coordinator.data["sensors"])
async_add_entities(
_create_miner_entity(key) for key in coordinator.data["miner_sensors"]
)
if coordinator.data["board_sensors"]:
for board in coordinator.data["board_sensors"]:
async_add_entities(
_create_board_entity(board, sensor)
for sensor in coordinator.data["board_sensors"][board]
)

@callback
def new_data_received():
"""Check for new sensors."""
entities = [
_create_entity(key)
for key in coordinator.data["sensors"]
if key not in created
_create_miner_entity(key)
for key in coordinator.data["miner_sensors"]
if key not in sensor_created
]
if entities:
async_add_entities(entities)

if coordinator.data["board_sensors"]:
for board in coordinator.data["board_sensors"]:
board_entities = [
_create_board_entity(board, sensor)
for sensor in coordinator.data["board_sensors"][board]
if f"{board}-{sensor}" not in sensor_created
]
if board_entities:
async_add_entities(board_entities)

coordinator.async_add_listener(new_data_received)


Expand All @@ -136,11 +188,12 @@ def __init__(
self._attr_unique_id = f"{self.coordinator.data['hostname']}-{key}"
self._key = key
self.entity_description = entity_description
self._attr_force_update = True

@property
def _sensor_data(self):
"""Return sensor data."""
return self.coordinator.data["sensors"][self._key]
return self.coordinator.data["miner_sensors"][self._key]

@property
def name(self) -> str | None:
Expand All @@ -155,95 +208,67 @@ def device_info(self) -> entity.DeviceInfo:
manufacturer="Antminer",
model=self.coordinator.data["model"],
name=f"Antminer {self.coordinator.data['model']}",
# sw_version=self.coordinator.data["version"],
)

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
# if self._key not in self.coordinator.data:
# if self._attr_unique_id:
# entity_registry.async_get(self.hass).async_remove(self.entity_id)
# else:
# self.hass.async_create_task(self.async_remove())
# return

super()._handle_coordinator_update()

# @property
# def extra_state_attributes(self) -> dict[str, str]:
# """Return the extra state attributes of the entity."""
# data = self._sensor_data
# attrs = {"type": data.getType()}
# if attrs["type"] == "Input":
# attrs["channel"] = data.getChannel()

# return attrs

@property
def native_value(self) -> StateType:
"""Return the state of the sensor."""
return self._sensor_data


# class MinerNumber(CoordinatorEntity[MinerCoordinator], NumberEntity):
# """Defines a Miner Sensor."""

# entity_description: MinerNumberEntityDescription

# def __init__(
# self,
# coordinator: MinerCoordinator,
# key: str,
# entity_description: MinerNumberEntityDescription,
# ) -> None:
# """Initialize the sensor."""
# super().__init__(coordinator=coordinator)

# self._key = key
# self.entity_description = entity_description

# @property
# def _sensor_data(self) -> Sensor:
# """Return sensor data."""
# return self.coordinator.data["sensors"][self._key]

# @property
# def name(self) -> str | None:
# """Return name of the entity."""
# return self._key

# @property
# def device_info(self) -> entity.DeviceInfo:
# """Return device info."""
# return entity.DeviceInfo(
# manufacturer="Antminer",
# model="S9",
# )

# @callback
# def _handle_coordinator_update(self) -> None:
# """Handle updated data from the coordinator."""
# if self._key not in self.coordinator.data:
# if self._attr_unique_id:
# entity_registry.async_get(self.hass).async_remove(self.entity_id)
# else:
# self.hass.async_create_task(self.async_remove())
# return

# super()._handle_coordinator_update()

# # @property
# # def extra_state_attributes(self) -> dict[str, str]:
# # """Return the extra state attributes of the entity."""
# # data = self._sensor_data
# # attrs = {"type": data.getType()}
# # if attrs["type"] == "Input":
# # attrs["channel"] = data.getChannel()

# # return attrs

# @property
# def native_value(self) -> StateType:
# """Return the state of the sensor."""
# return self._sensor_data
class MinerBoardSensor(CoordinatorEntity[MinerCoordinator], SensorEntity):
"""Defines a Miner Sensor."""

entity_description: MinerSensorEntityDescription

def __init__(
self,
coordinator: MinerCoordinator,
board: str,
sensor: str,
entity_description: MinerSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator=coordinator)
self._attr_unique_id = f"{self.coordinator.data['hostname']}-{board}-{sensor}"
self._board = board
self._sensor = sensor
self.entity_description = entity_description
self._attr_force_update = True

@property
def _sensor_data(self):
"""Return sensor data."""
return self.coordinator.data["board_sensors"][self._board][self._sensor]

@property
def name(self) -> str | None:
"""Return name of the entity."""
return f"{self.coordinator.data['hostname']} Board #{self._board} {self.entity_description.key}"

@property
def device_info(self) -> entity.DeviceInfo:
"""Return device info."""
return entity.DeviceInfo(
identifiers={(DOMAIN, self.coordinator.data["hostname"])},
manufacturer="Antminer",
model=self.coordinator.data["model"],
name=f"Antminer {self.coordinator.data['model']}",
)

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""

super()._handle_coordinator_update()

@property
def native_value(self) -> StateType:
"""Return the state of the sensor."""
return self._sensor_data
4 changes: 2 additions & 2 deletions custom_components/miner/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def __init__(
"""Initialize the sensor."""
super().__init__(coordinator=coordinator)
self._attr_unique_id = f"{self.coordinator.data['hostname']}-active"
self._attr_is_on = self.coordinator.data["sensors"]["temperature"] != 0
self._attr_is_on = self.coordinator.data["miner_sensors"]["temperature"] != 0

@property
def name(self) -> str | None:
Expand Down Expand Up @@ -121,6 +121,6 @@ def _handle_coordinator_update(self) -> None:

# There isn't really a good way to check if the Miner is on.
# But when it's off there is no temperature reported, so we use this
self._attr_is_on = self.coordinator.data["sensors"]["temperature"] != 0
self._attr_is_on = self.coordinator.data["miner_sensors"]["temperature"] != 0

super()._handle_coordinator_update()

0 comments on commit ad108dd

Please sign in to comment.