Skip to content

Commit

Permalink
Add new devices for rear speakers #11
Browse files Browse the repository at this point in the history
  • Loading branch information
MrBearPresident committed Jan 5, 2025
1 parent 8d3b297 commit e104a85
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 16 deletions.
3 changes: 2 additions & 1 deletion custom_components/jbl_integration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {"coordinator": coordinator}

await hass.config_entries.async_forward_entry_setups(entry, ["sensor", "switch","number","button"])
await hass.config_entries.async_forward_entry_setups(entry, ["sensor", "switch","number","button","binary_sensor"])


return True
Expand All @@ -40,6 +40,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
await hass.config_entries.async_forward_entry_unload(entry, "sensor")
await hass.config_entries.async_forward_entry_unload(entry, "number")
await hass.config_entries.async_forward_entry_unload(entry, "button")
await hass.config_entries.async_forward_entry_unload(entry, "binary_sensor")

hass.data[DOMAIN].pop(entry.entry_id)
return True
176 changes: 176 additions & 0 deletions custom_components/jbl_integration/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
"""Binary Sensor platform for JBL integration."""
import logging
from homeassistant.components.binary_sensor import BinarySensorEntity, BinarySensorDeviceClass
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities):
"""Set up the JBL binary sensor platform."""
coordinator = hass.data[DOMAIN][entry.entry_id]["coordinator"]

if "Rears" in coordinator.data:
async_add_entities([
JBLChargingSensor(coordinator, entry, 0),
JBLDockedSensor(coordinator, entry, 0),
JBLOnlineSensor(coordinator, entry, 0),
JBLChargingSensor(coordinator, entry, 1),
JBLDockedSensor(coordinator, entry, 1),
JBLOnlineSensor(coordinator, entry, 1),
])

class JBLChargingSensor(BinarySensorEntity):
"""Representation of a charging sensor for the JBL rear speakers."""

def __init__(self, coordinator, entry, arrayNumber: int):
"""Initialize the sensor."""
self.coordinator = coordinator
self.number = arrayNumber
self._entry = entry
self.entityName = "Charging"
self.entity_id = f"binary_sensor.{self.coordinator.device_info.get('name', 'jbl_integration').replace(' ', '_').lower()}_{self.entityName.replace(' ', '_').lower()}"

@property
def name(self):
"""Return the name of the sensor."""
return self.entityName

@property
def is_on(self):
"""Return true if the sensor is on."""
return self.coordinator.data["Rears"][self.number]["charging"]

@property
def device_class(self):
return BinarySensorDeviceClass.BATTERY_CHARGING

@property
def unique_id(self):
"""Return a unique ID for the sensor."""
return f"jbl_{self._entry.entry_id}_{self.coordinator.data["Rears"][self.number]["channel"].lower()}_{self.entityName.replace(' ', '_').lower()}"

@property
def should_poll(self):
"""No polling needed."""
return False

@property
def device_info(self):
"""Return device information about this entity."""
BaseDevice = self.coordinator.device_info.copy()
BaseDevice["name"] = f"{BaseDevice['name']} Rear Speaker {self.coordinator.data['Rears'][self.number]['channel'].title()}"
BaseDevice["identifiers"] = {(DOMAIN, f"{self.coordinator.device_info.get('name', 'jbl_integration').replace(' ', '_').lower()}_{self.coordinator.data['Rears'][self.number]['channel']}")}
return BaseDevice

async def async_added_to_hass(self):
"""When entity is added to hass."""
self.async_on_remove(self.coordinator.async_add_listener(self.async_write_ha_state))

async def async_update(self):
"""Update the sensor."""
await self.coordinator.async_request_refresh()

class JBLDockedSensor(BinarySensorEntity):
"""Representation of a docked sensor for the JBL rear speakers."""

def __init__(self, coordinator, entry, arrayNumber: int):
"""Initialize the sensor."""
self.coordinator = coordinator
self.number = arrayNumber
self._entry = entry
self.entityName = "Docked"
self.entity_id = f"binary_sensor.{self.coordinator.device_info.get('name', 'jbl_integration').replace(' ', '_').lower()}_{self.entityName.replace(' ', '_').lower()}"

@property
def name(self):
"""Return the name of the sensor."""
return self.entityName

@property
def is_on(self):
"""Return true if the sensor is on."""
return self.coordinator.data["Rears"][self.number]["docked"]

@property
def device_class(self):
return BinarySensorDeviceClass.PLUG

@property
def unique_id(self):
"""Return a unique ID for the sensor."""
return f"jbl_{self._entry.entry_id}_{self.coordinator.data["Rears"][self.number]["channel"].lower()}_{self.entityName.replace(' ', '_').lower()}"

@property
def should_poll(self):
"""No polling needed."""
return False

@property
def device_info(self):
"""Return device information about this entity."""
BaseDevice = self.coordinator.device_info.copy()
BaseDevice["name"] = f"{BaseDevice['name']} Rear Speaker {self.coordinator.data['Rears'][self.number]['channel'].title()}"
BaseDevice["identifiers"] = {(DOMAIN, f"{self.coordinator.device_info.get('name', 'jbl_integration').replace(' ', '_').lower()}_{self.coordinator.data['Rears'][self.number]['channel']}")}
return BaseDevice

async def async_added_to_hass(self):
"""When entity is added to hass."""
self.async_on_remove(self.coordinator.async_add_listener(self.async_write_ha_state))

async def async_update(self):
"""Update the sensor."""
await self.coordinator.async_request_refresh()

class JBLOnlineSensor(BinarySensorEntity):
"""Representation of an online sensor for the JBL rear speakers."""

def __init__(self, coordinator, entry, arrayNumber: int):
"""Initialize the sensor."""
self.coordinator = coordinator
self.number = arrayNumber
self._entry = entry
self.entityName = "Online"
self.entity_id = f"binary_sensor.{self.coordinator.device_info.get('name', 'jbl_integration').replace(' ', '_').lower()}_{self.entityName.replace(' ', '_').lower()}"

@property
def name(self):
"""Return the name of the sensor."""
return self.entityName

@property
def is_on(self):
"""Return true if the sensor is on."""
return self.coordinator.data["Rears"][self.number]["status"] == "online"

@property
def device_class(self):
return BinarySensorDeviceClass.CONNECTIVITY

@property
def unique_id(self):
"""Return a unique ID for the sensor."""
return f"jbl_{self._entry.entry_id}_{self.coordinator.data["Rears"][self.number]["channel"].lower()}_{self.entityName.replace(' ', '_').lower()}"

@property
def should_poll(self):
"""No polling needed."""
return False

@property
def device_info(self):
"""Return device information about this entity."""
BaseDevice = self.coordinator.device_info.copy()
BaseDevice["name"] = f"{BaseDevice['name']} Rear Speaker {self.coordinator.data['Rears'][self.number]['channel'].title()}"
BaseDevice["identifiers"] = {(DOMAIN, f"{self.coordinator.device_info.get('name', 'jbl_integration').replace(' ', '_').lower()}_{self.coordinator.data['Rears'][self.number]['channel']}")}
return BaseDevice

async def async_added_to_hass(self):
"""When entity is added to hass."""
self.async_on_remove(self.coordinator.async_add_listener(self.async_write_ha_state))

async def async_update(self):
"""Update the sensor."""
await self.coordinator.async_request_refresh()
32 changes: 31 additions & 1 deletion custom_components/jbl_integration/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,13 @@ async def _async_update_data(self):
data1 = await self.requestInfo()
data2 = await self.getEQ()
data3 = await self.getNightMode()
data4 = await self.getRearSpeaker()

data12 = self.merge_two_dicts(data1, data2)
data123 = self.merge_two_dicts(data12, data3)
data1234 = self.merge_two_dicts(data123, data4)

combined_data = data123
combined_data = data1234
# Ensure self.data is initialized to an empty dictionary if it is None
if self.data is None:
self.data = {}
Expand Down Expand Up @@ -434,6 +436,34 @@ async def setNightMode(self, value: bool):
except Exception as e:
_LOGGER.error("Error setting Nightmode: %s", str(e))
return {}

async def getRearSpeaker(self):
# Disable SSL warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

url = f'https://{self.address}/httpapi.asp?command=getRearSpeakerStatus'

headers = {
'Accept-Encoding': "gzip",
}
async with aiohttp.ClientSession() as session:
try:
with async_timeout.timeout(10):
async with session.get(url, headers=headers, ssl=self.sslcontext) as response:
if response.status == 200:
response_text = await response.text()
response_json = json.loads(response_text)
_LOGGER.debug("Rear Speakers Response text: %s", response_text)
try:
return {"Rears":response_json["rears"]}
except Exception as e:
_LOGGER.debug("No Rear Speakers available")
return {}
else:
return {}
except Exception as e:
_LOGGER.error("Error getting Rear Speakers: %s", str(e))
return {}

def merge_two_dicts(self,x, y):
z = x.copy() # start with keys and values of x
Expand Down
2 changes: 1 addition & 1 deletion custom_components/jbl_integration/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def name(self):

@property
def icon(self):
return " mdi:equalizer"
return "mdi:equalizer"

@property
def unique_id(self):
Expand Down
99 changes: 86 additions & 13 deletions custom_components/jbl_integration/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.const import PERCENTAGE
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)

from .const import DOMAIN
from .coordinator import Coordinator
Expand All @@ -17,19 +24,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e
"""Set up the JBL sensor platform."""

coordinator = hass.data[DOMAIN][entry.entry_id]["coordinator"]

async_add_entities([
JBLSensor(coordinator,entry,"play_medium","Play Medium","mdi:soundbar"),
JBLSensor(coordinator,entry,"volume_level","Volume","mdi:volume-high"),
JBLSensor(coordinator,entry,"transport_state","Transport State","mdi:state-machine"),
JBLSensor(coordinator,entry,"transport_status","Transport Status","mdi:information"),
JBLSensor(coordinator,entry,"mute","mute","mdi:volume-mute"),
JBLSensor(coordinator,entry,"track_duration","Track Duration","mdi:information"),
JBLSensor(coordinator,entry,"track","Track","mdi:information"),
JBLSensor(coordinator,entry,"channel","Channel","mdi:information"),
JBLSensor(coordinator,entry,"slaves","Slaves Present","mdi:information"),
# Add other sensors here
])

entityArray = []
entityArray.append(JBLSensor(coordinator,entry,"play_medium","Play Medium","mdi:soundbar"))
entityArray.append(JBLSensor(coordinator,entry,"volume_level","Volume","mdi:volume-high"))
entityArray.append(JBLSensor(coordinator,entry,"transport_state","Transport State","mdi:state-machine"))
entityArray.append(JBLSensor(coordinator,entry,"transport_status","Transport Status","mdi:information"))
entityArray.append(JBLSensor(coordinator,entry,"mute","mute","mdi:volume-mute"))
entityArray.append(JBLSensor(coordinator,entry,"track_duration","Track Duration","mdi:information"))
entityArray.append(JBLSensor(coordinator,entry,"track","Track","mdi:information"))
entityArray.append(JBLSensor(coordinator,entry,"channel","Channel","mdi:information"))

if "Rears" in coordinator.data:
entityArray.append(JBLRearSensor(coordinator,entry,0))
entityArray.append(JBLRearSensor(coordinator,entry,1))

async_add_entities(entityArray)


class JBLSensor(Entity):
Expand Down Expand Up @@ -92,3 +102,66 @@ async def async_update(self):
"""Update the sensor."""
await self.coordinator.async_request_refresh()

class JBLRearSensor(Entity):
"""Representation of a battery for the rear speakers of the JBL."""

def __init__(self, coordinator, entry, arrayNumber: int):
"""Initialize the sensor."""
self.coordinator = coordinator
self.number = arrayNumber
self._entry = entry
self.entityName = "Battery"
self.entity_id = f"sensor.{self.coordinator.device_info.get("name", "jbl_integration").replace(' ', '_').lower()}_{self.entityName.replace(' ', '_').lower()}"

@property
def name(self):
"""Return the name of the sensor."""
return self.entityName

@property
def device_class(self):
return SensorDeviceClass.BATTERY

@property
def unit_of_measurement(self):
return PERCENTAGE

@property
def entity_registry_enabled_default(self) -> bool:
"""Return whether the entity should be enabled when first added to the entity registry."""
return True # Disable the sensor by default

@property
def state(self):
"""Return the state of the sensor."""
return self.coordinator.data["Rears"][self.number]["capicity"]

@property
def enabled(self):
return True

@property
def unique_id(self):
"""Return a unique ID for the sensor."""
return f"jbl_{self._entry.entry_id}_{self.coordinator.data["Rears"][self.number]["channel"].lower()}_{self.entityName.replace(' ', '_').lower()}"

@property
def should_poll(self):
"""No polling needed."""
return False

@property
def device_info(self):
"""Return device information about this entity."""
BaseDevice = self.coordinator.device_info.copy()
BaseDevice["name"] = BaseDevice["name"] + " Rear Speaker " + self.coordinator.data["Rears"][self.number]["channel"].title()
BaseDevice["identifiers"] = {(DOMAIN,f"{self.coordinator.device_info.get("name", "jbl_integration").replace(' ', '_').lower()}_{self.coordinator.data["Rears"][self.number]["channel"]}")}
return BaseDevice

async def async_added_to_hass(self):
"""When entity is added to hass."""
self.async_on_remove(self.coordinator.async_add_listener(self.async_write_ha_state))

async def async_update(self):
"""Update the sensor."""
await self.coordinator.async_request_refresh()

0 comments on commit e104a85

Please sign in to comment.