Skip to content

Commit

Permalink
Merge pull request #7 from sangvikh/dev
Browse files Browse the repository at this point in the history
v2.0.0
  • Loading branch information
sangvikh committed Sep 1, 2024
2 parents d4ad884 + edd966b commit 47f7029
Show file tree
Hide file tree
Showing 12 changed files with 553 additions and 128 deletions.
30 changes: 22 additions & 8 deletions custom_components/hass_pontos/__init__.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,41 @@
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry
from .services import register_services

from .services import register_services
from .device import register_device
from .const import DOMAIN

platforms = ['sensor', 'button', 'valve', 'select']

async def async_setup(hass: HomeAssistant, config: dict):
return True

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = entry.data

# Register sensors
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, 'sensor')
)
# Register the device
await register_device(hass, entry)

# Register services
await register_services(hass)

# Register entities for each platform
hass.async_create_task(
hass.config_entries.async_forward_entry_setups(entry, platforms)
)

return True

async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
await hass.config_entries.async_forward_entry_unload(entry, 'sensor')
hass.data[DOMAIN].pop(entry.entry_id)
return True
# Unload each platform
unload_ok = all(
await hass.config_entries.async_forward_entry_unload(entry, platform)
for platform in platforms
)

# Remove data related to this entry if everything is unloaded successfully
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok
48 changes: 48 additions & 0 deletions custom_components/hass_pontos/button.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# custom_components/hass_pontos/button.py
import logging
from homeassistant.components.button import ButtonEntity
from homeassistant.util import slugify
from .const import DOMAIN
from .device import get_device_info

LOGGER = logging.getLogger(__name__)

async def async_setup_entry(hass, entry, async_add_entities):
# Fetch device info
device_info, _ = await get_device_info(hass, entry)

# Instantiate button
reset_button = PontosClearAlarmsButton(hass, entry, device_info)

# Add entities
async_add_entities([reset_button], True)

class PontosClearAlarmsButton(ButtonEntity):
"""Button to clear alarms on the Pontos Base device."""

def __init__(self, hass, entry, device_info):
"""Initialize the button."""
self._hass = hass
self._entry = entry
self._attr_name = f"{device_info['name']} Clear alarms"
self._attr_unique_id = slugify(f"{device_info['serial_number']}_clear_alarms")
self._device_info = device_info

async def async_press(self):
"""Handle the button press to clear alarms."""
LOGGER.info("Clear Alarms button pressed")
await self._hass.services.async_call(
DOMAIN,
"clear_alarms", # Assuming the service name for clearing alarms is "clear_alarms"
service_data={"entry_id": self._entry.entry_id}
)

@property
def unique_id(self):
return self._attr_unique_id

@property
def device_info(self):
return {
"identifiers": self._device_info['identifiers'],
}
17 changes: 11 additions & 6 deletions custom_components/hass_pontos/const.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import timedelta
from homeassistant.components.valve import STATE_OPEN, STATE_OPENING, STATE_CLOSED, STATE_CLOSING

DOMAIN = "hass_pontos"
CONF_IP_ADDRESS = "ip_address"
Expand Down Expand Up @@ -43,10 +44,10 @@
}

VALVE_CODES = {
"10": "Closed",
"11": "Closing",
"20": "Open",
"21": "Opening"
"10": STATE_CLOSED,
"11": STATE_CLOSING,
"20": STATE_OPEN,
"21": STATE_OPENING
}

SENSOR_DETAILS = {
Expand Down Expand Up @@ -168,8 +169,12 @@
"name": "Close valve",
"endpoint": "set/ab/2"
},
"clear_alarm": {
"clear_alarms": {
"name": "Clear alarms",
"endpoint": "clr/ala"
},
}
"set_profile": {
"name": "Set Profile",
"endpoint": "set/prf/{profile_number}"
},
}
69 changes: 69 additions & 0 deletions custom_components/hass_pontos/device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import logging
import time
from homeassistant.helpers.device_registry import async_get as async_get_device_registry
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC

from .const import DOMAIN, URL_LIST, CONF_IP_ADDRESS, FETCH_INTERVAL
from .utils import fetch_data

LOGGER = logging.getLogger(__name__)

# Cache to store device data
_device_cache = {}

async def get_device_info(hass, entry):
entry_id = entry.entry_id
ip_address = entry.data[CONF_IP_ADDRESS]

# Check if the data is already cached and not expired
if entry_id in _device_cache:
cache_entry = _device_cache[entry_id]
cache_age = time.time() - cache_entry['timestamp']
if cache_age < FETCH_INTERVAL.total_seconds():
LOGGER.debug(f"Using cached data for device {entry_id} (age: {cache_age} seconds)")
return cache_entry['device_info'], cache_entry['data']
else:
LOGGER.debug(f"Cache expired for device {entry_id} (age: {cache_age} seconds)")

LOGGER.debug(f"Fetching data for device {entry_id} from the device")

# Fetching all relevant data from the device
data = await fetch_data(ip_address, URL_LIST)

# Assign data to variables
mac_address = data.get("getMAC", "00:00:00:00:00:00:00:00")
serial_number = data.get("getSRN", "")
firmware_version = data.get("getVER", "")
device_type = data.get("getTYP", "")

device_info = {
"identifiers": {(DOMAIN, "pontos_base")},
"connections": {(CONNECTION_NETWORK_MAC, mac_address)},
"name": "Pontos Base",
"manufacturer": "Hansgrohe",
"model": device_type,
"sw_version": firmware_version,
"serial_number": serial_number,
}

# Cache the device info and data
_device_cache[entry_id] = {
'device_info': device_info,
'data': data,
'timestamp': time.time()
}

return device_info, data

async def register_device(hass, entry):
entry_id = entry.entry_id

# Create a device entry with fetched data
device_registry = async_get_device_registry(hass)
device_info, _ = await get_device_info(hass, entry)

# Register device in the device registry
device_registry.async_get_or_create(
config_entry_id=entry_id,
**device_info
)
10 changes: 5 additions & 5 deletions custom_components/hass_pontos/manifest.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"domain": "hass_pontos",
"name": "Hansgrohe Pontos",
"domain": "hass_pontos",
"name": "Hansgrohe Pontos",
"codeowners": [
"@sangvikh"
],
"config_flow": true,
"dependencies": [],
"documentation": "https://github.com/sangvikh/hass-pontos",
"iot_class": "local_polling",
"issue_tracker": "https://github.com/sangvikh/hass-pontos/issues",
"issue_tracker": "https://github.com/sangvikh/hass-pontos/issues",
"requirements": [],
"version": "1.0.4"
}
"version": "2.0.0"
}
100 changes: 100 additions & 0 deletions custom_components/hass_pontos/select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import logging
from homeassistant.components.select import SelectEntity
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.event import async_track_state_change_event
from homeassistant.core import callback, Event
from .device import get_device_info
from .const import DOMAIN, PROFILE_CODES

LOGGER = logging.getLogger(__name__)

async def async_setup_entry(hass, entry, async_add_entities):
"""Set up the custom profile select entity."""
device_info, data = await get_device_info(hass, entry)
select_entity = PontosProfileSelect(hass, entry, device_info)
async_add_entities([select_entity], True)

class PontosProfileSelect(SelectEntity):
"""Representation of a Select entity for setting profiles."""

def __init__(self, hass, entry, device_info):
"""Initialize the profile select entity."""
self._hass = hass
self._entry = entry
self._attr_name = f"{device_info['name']} Profile"
self._attr_unique_id = f"{device_info['serial_number']}_profile_select"
self._sensor_unique_id = f"{device_info['serial_number']}_active_profile"
self._attr_options = [
name if name else "Not Defined"
for name in PROFILE_CODES.values()
if name and name != "not defined"
]
self._attr_current_option = None
self._device_info = device_info

async def async_added_to_hass(self):
"""When entity is added to hass."""
await super().async_added_to_hass()

# Get the entity ID of the sensor using the unique ID
entity_registry = er.async_get(self.hass)
sensor_entity_id = entity_registry.async_get_entity_id("sensor", DOMAIN, self._sensor_unique_id)

# Fetch the initial state from the sensor entity
if sensor_entity_id:
sensor_state = self.hass.states.get(sensor_entity_id)
initial_state = sensor_state.state if sensor_state else None
LOGGER.debug(f"Fetched initial profile state from sensor: {initial_state}")
self._attr_current_option = initial_state
self.async_write_ha_state()

# Register state change listener
LOGGER.debug(f"Registering state change listener for {sensor_entity_id}")
async_track_state_change_event(
self.hass,
sensor_entity_id,
self._sensor_state_changed
)
else:
LOGGER.error(f"Sensor with unique ID {self._sensor_unique_id} not found")

@callback
def _sensor_state_changed(self, event: Event) -> None:
"""Handle active profile sensor state changes."""
new_state = event.data.get('new_state')
if new_state is not None:
new_option = new_state.state
if new_option != self._attr_current_option:
LOGGER.debug(f"Profile state changed to: {new_option}")
self.set_state(new_option)

def set_state(self, state):
"""Set the valve state and update Home Assistant."""
self._attr_current_option = state
self.async_write_ha_state()

async def async_select_option(self, option: str):
"""Handle the user selecting an option."""
LOGGER.info(f"Setting profile to {option}")
profile_number = self.map_profile_name_to_number(option)
await self._hass.services.async_call(
DOMAIN,
"set_profile",
service_data={"profile_number": profile_number, "ip_address": self._entry.data["ip_address"]}
)
self._attr_current_option = option
self.async_write_ha_state()

@property
def device_info(self):
"""Return device info to link this entity with the device."""
return {
"identifiers": self._device_info['identifiers'],
}

def map_profile_name_to_number(self, profile_name):
"""Map profile name to profile number."""
for number, name in PROFILE_CODES.items():
if name == profile_name:
return int(number)
return None
Loading

0 comments on commit 47f7029

Please sign in to comment.