Skip to content

Commit 1c70771

Browse files
committed
V1.0.0
1 parent a6160c5 commit 1c70771

6 files changed

Lines changed: 75 additions & 77 deletions

File tree

custom_components/bituopmd/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
_LOGGER = logging.getLogger(__name__)
1111

12-
PLATFORMS = [Platform.SENSOR, Platform.BUTTON, Platform.SWITCH]
12+
PLATFORMS = [Platform.BUTTON, Platform.SWITCH]
1313

1414
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
1515
"""Set up BituoPMD integration from a config entry."""
@@ -39,7 +39,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
3939
entry, title=f"{model_id} - {host_ip}"
4040
)
4141

42-
# Forward the setup to the sensor and control platforms
42+
# Forward the setup to the sensor platforms
43+
try:
44+
await hass.config_entries.async_forward_entry_setup(entry, Platform.SENSOR)
45+
except ConfigEntryNotReady as e:
46+
_LOGGER.error("Error setting up sensor platform for BituoPMD: %s", e)
47+
raise ConfigEntryNotReady from e
48+
4349
try:
4450
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
4551
except ConfigEntryNotReady as e:

custom_components/bituopmd/button.py

Lines changed: 30 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -18,80 +18,43 @@
1818
async def async_setup_entry(hass, entry, async_add_entities):
1919
"""Set up button platform."""
2020
host_ip = entry.data[CONF_HOST_IP]
21-
coordinator = BituoDataUpdateCoordinator(hass, host_ip)
2221
try:
23-
await coordinator.async_config_entry_first_refresh()
24-
except UpdateFailed as e:
25-
_LOGGER.error("Error initializing button platform: %s", e)
26-
raise ConfigEntryNotReady from e
27-
28-
# Fetch device model and firmware version
29-
try:
30-
device_info = await coordinator.fetch_device_info()
31-
except UpdateFailed:
32-
_LOGGER.error("Failed to fetch device info for %s", host_ip)
33-
device_info = {"model": "Unknown Model", "fw_version": "Unknown", "manufacturer": "Unknown", "MCUVersion": "Unknown", "manufacturer": "Unknown", "mcu_version": "Unknown"}
34-
35-
buttons = []
22+
response = await hass.async_add_executor_job(
23+
requests.get, f"http://{host_ip}/data"
24+
)
25+
data = response.json()
26+
device_info = {
27+
"model": data.get("productModel") or data.get("ProductModel", "Unknown Model"),
28+
"fw_version": data.get("FWVersion") or data.get("fwVersion", "Unknown"),
29+
"manufacturer": data.get("Manufactor", "Unknown"),
30+
"mcu_version": data.get("MCUVersion", "Unknown"),
31+
}
32+
except Exception as e:
33+
_LOGGER.error("Failed to fetch device info for %s: %s", host_ip, e)
34+
device_info = {
35+
"model": "Unknown Model",
36+
"fw_version": "Unknown",
37+
"manufacturer": "Unknown",
38+
"mcu_version": "Unknown",
39+
}
3640

3741
sensor_coordinator = hass.data[DOMAIN][entry.entry_id]['sensor_coordinator']
38-
buttons.append(DataRefreshButton(sensor_coordinator, host_ip, device_info["model"], device_info["fw_version"], device_info["manufacturer"], device_info["mcu_version"]))
39-
40-
if coordinator.data:
41-
for field, action in coordinator.data.items():
42-
if "switch" in field.lower():
43-
continue # Skip buttons with 'switch' in the name
44-
if field == "zero":
45-
field = "zero_Energy" # Rename 'zero' to 'zeroenergy'
46-
buttons.append(BituoButton(coordinator, host_ip, field, action, device_info["model"], device_info["fw_version"], device_info["manufacturer"], device_info["mcu_version"]))
4742

48-
async_add_entities(buttons, True)
49-
50-
class BituoDataUpdateCoordinator(DataUpdateCoordinator):
51-
"""Class to manage fetching data from the device."""
52-
53-
def __init__(self, hass, host_ip):
54-
"""Initialize."""
55-
self.host_ip = host_ip
56-
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL)
57-
58-
async def _async_update_data(self):
59-
"""Fetch data from the device."""
60-
try:
61-
response = await self.hass.async_add_executor_job(
62-
requests.get, f"http://{self.host_ip}/hadata"
63-
)
64-
return response.json()
65-
except Exception as err:
66-
raise UpdateFailed(f"Error communicating with API: {err}")
43+
buttons = [
44+
DataRefreshButton(sensor_coordinator, host_ip, device_info["model"], device_info["fw_version"], device_info["manufacturer"], device_info["mcu_version"]),
45+
DeviceLocatingButton(host_ip, device_info["model"], device_info["fw_version"], device_info["manufacturer"], device_info["mcu_version"])
46+
]
6747

68-
async def fetch_device_info(self):
69-
"""Fetch device model and firmware version information."""
70-
try:
71-
response = await self.hass.async_add_executor_job(
72-
requests.get, f"http://{self.host_ip}/data"
73-
)
74-
data = response.json()
75-
return {
76-
"model": data.get("productModel") or data.get("ProductModel", "Unknown Model"),
77-
"fw_version": data.get("FWVersion") or data.get("fwVersion", "Unknown"),
78-
"manufacturer": data.get("Manufactor", "Unknown"),
79-
"mcu_version": data.get("MCUVersion", "Unknown"),
80-
}
81-
except Exception as err:
82-
raise UpdateFailed(f"Error fetching device info: {err}")
48+
async_add_entities(buttons, True)
8349

84-
class BituoButton(CoordinatorEntity, ButtonEntity):
50+
class DeviceLocatingButton(ButtonEntity):
8551
"""Representation of a Button."""
8652

87-
def __init__(self, coordinator, host_ip, field, action, model, fw_version, manufacturer, mcu_version):
53+
def __init__(self, host_ip, model, fw_version, manufacturer, mcu_version):
8854
"""Initialize the button."""
89-
super().__init__(coordinator)
90-
self._field = field
91-
self._action = action
92-
self._attr_name = f"{field.replace('_', ' ').title()}"
93-
self._attr_unique_id = f"{host_ip}_{field}"
94-
self.entity_id = f"button.{host_ip.replace('.', '_')}_{self.format_field_entity_id(field)}"
55+
self._attr_name = "Device Locating"
56+
self._attr_unique_id = f"{host_ip}_device_locating"
57+
self.entity_id = f"button.{host_ip.replace('.', '_')}_device_locating"
9558
self._attr_device_info = DeviceInfo(
9659
identifiers={(DOMAIN, host_ip)},
9760
name=f"{model} - {host_ip}",
@@ -101,11 +64,12 @@ def __init__(self, coordinator, host_ip, field, action, model, fw_version, manuf
10164
configuration_url=f"http://{host_ip}" # embed URL
10265
)
10366
self._host_ip = host_ip
67+
self._attr_icon = "mdi:map-marker"
10468

10569
async def async_press(self):
10670
"""Handle the button press."""
10771
await self.hass.async_add_executor_job(
108-
requests.get, f"http://{self._host_ip}/{self._action}"
72+
requests.get, f"http://{self._host_ip}/location"
10973
)
11074

11175
@staticmethod

custom_components/bituopmd/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"domain": "bituopmd",
33
"name": "BituoPMD",
4-
"version": "0.0.1",
4+
"version": "1.0.0",
55
"config_flow": true,
66
"zeroconf": ["_http._tcp.local."],
77
"documentation": "https://github.com/script0803/BituoPMD",
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"SPM01": "1.1.6",
33
"SPM02": "1.1.6",
4-
"SDM01": "3.3.3",
5-
"SDM02": "1.1.1"
4+
"SDM01": "3.3.4",
5+
"SDM02": "1.1.1",
6+
"common": "4.1.1"
67
}

custom_components/bituopmd/sensor.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ async def _async_update_data(self):
180180
)
181181
response.raise_for_status()
182182
data = response.json()
183+
# _LOGGER.debug(f"{self.host_ip} - {data}")
183184

184185
# Multiply power values by 1000
185186
for key in data:
@@ -218,9 +219,13 @@ async def check_for_ota_updates(self):
218219
return
219220

220221
device_info = await self.fetch_device_info()
221-
model = device_info.get("model", "Unknown Model")
222222
current_version = device_info.get("fw_version", "Unknown")
223-
latest_version = self.ota_versions.get(model, "Unknown")
223+
224+
if current_version != "Unknown" and int(current_version.split('.')[0]) >= 4:
225+
latest_version = self.ota_versions.get("common", "Unknown")
226+
else:
227+
model = device_info.get("model", "Unknown Model")
228+
latest_version = self.ota_versions.get(model, "Unknown")
224229

225230
if latest_version != "Unknown" and current_version != "Unknown" and version.parse(current_version) < version.parse(latest_version):
226231
self.ota_entity._attr_state = "OTA Available"
@@ -264,6 +269,8 @@ def __init__(self, coordinator, host_ip, field, model, fw_version, manufacturer,
264269
self._attr_suggested_display_precision = 0
265270
if "power" in self._field.lower() and "apparent" in self._field.lower():
266271
self._attr_suggested_display_precision = 0
272+
if "unbalancelinecurrents" in self._field.lower():
273+
self._attr_suggested_display_precision = 0
267274

268275
def get_initial_unit_of_measurement(self):
269276
"""Determine the initial unit of measurement based on the field."""

custom_components/bituopmd/www/bituo_panel.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,26 @@ class BituoPanel extends HTMLElement {
373373
});
374374
}
375375
}
376+
377+
showConfirmationDialog(message, onConfirm) {
378+
const dialog = this.querySelector('#confirmation-dialog');
379+
const messageElement = this.querySelector('#confirmation-message');
380+
const yesButton = this.querySelector('#confirm-yes');
381+
const noButton = this.querySelector('#confirm-no');
382+
383+
messageElement.textContent = message;
384+
dialog.style.display = 'block';
385+
386+
yesButton.onclick = () => {
387+
dialog.style.display = 'none';
388+
onConfirm();
389+
};
390+
391+
noButton.onclick = () => {
392+
dialog.style.display = 'none';
393+
this.hideOtaOverlay();
394+
};
395+
}
376396

377397
async setDataRequestFrequency() {
378398
const frequency = parseInt(this.querySelector('#data-frequency').value);
@@ -411,9 +431,8 @@ class BituoPanel extends HTMLElement {
411431
// 限制并发数量
412432
await this.runWithConcurrencyLimit(onlineTasks, 10);
413433

414-
const totalDevices = options.length;
415-
const offlineCount = offlineDevices.length;
416-
this.showAlert(`Total devices: ${totalDevices}. Offline devices: ${offlineCount}. Polling interval has been successfully updated for all online devices.`);
434+
const onlineDevices = options.length - offlineDevices.length;
435+
this.showAlert(`Polling interval has been successfully updated for ${onlineDevices} online devices.`);
417436
} else {
418437
const { deviceIp } = this.getSelectedDevice();
419438
if (!deviceIp) {
@@ -654,7 +673,7 @@ class BituoPanel extends HTMLElement {
654673
</div>
655674
`;
656675
}
657-
676+
658677
async performGetAction(action) {
659678
const { deviceIp } = this.getSelectedDevice();
660679
if (!deviceIp) {
@@ -699,6 +718,7 @@ class BituoPanel extends HTMLElement {
699718

700719
} else {
701720
const response = await this._hass.callApi('POST', `bituopmd/proxy/${deviceIp}/${action}`, body);
721+
this.showAlert(response.response);
702722
}
703723
} catch (error) {
704724
this.showAlert(`Error: ${error.message}`);

0 commit comments

Comments
 (0)