Skip to content

Commit

Permalink
Merge pull request #31 from Crewski/schedules
Browse files Browse the repository at this point in the history
Schedules
  • Loading branch information
Crewski authored May 23, 2023
2 parents 7cd9a72 + 017f1d5 commit b5f3e85
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 0 deletions.
6 changes: 6 additions & 0 deletions custom_components/njspc_ha/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
EVENT_FILTER,
EVENT_VIRTUAL_CIRCUIT,
EVENT_TEMPS,
EVENT_SCHEDULE,
)


Expand Down Expand Up @@ -187,6 +188,11 @@ async def handle_virtual_circuit(data):
data["event"] = EVENT_VIRTUAL_CIRCUIT
self.async_set_updated_data(data)

@self.sio.on("schedule")
async def handle_schedule(data):
data["event"] = EVENT_SCHEDULE
self.async_set_updated_data(data)

@self.sio.event
async def connect():
print("I'm connected!")
Expand Down
2 changes: 2 additions & 0 deletions custom_components/njspc_ha/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
API_TEMPERATURE_SETPOINT = "state/body/setPoint"
API_SET_HEATMODE = "state/body/heatMode"
API_CHEM_CONTROLLER_SETPOINT = "state/chemController"
API_CONFIG_SCHEDULE = "config/schedule"

# SOCKETIO EVENTS
EVENT_CIRCUIT = "circuit"
Expand All @@ -42,6 +43,7 @@
EVENT_CHEM_CONTROLLER = "chemController"
EVENT_FILTER = "filter"
EVENT_VIRTUAL_CIRCUIT = "virtualCircuit"
EVENT_SCHEDULE = "schedule"

POOL_SETPOINT = "poolSetpoint"
SPA_SETPOINT = "spaSetpoint"
Expand Down
5 changes: 5 additions & 0 deletions custom_components/njspc_ha/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ def format_duration(self, secs: int) -> str:
formatted = f"{formatted} {sec}sec"
return formatted






@property
def device_info(self) -> DeviceInfo | None:
"""Device info"""
Expand Down
202 changes: 202 additions & 0 deletions custom_components/njspc_ha/schedules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
"""Platform for schedule integration."""
from __future__ import annotations

from typing import Any
from collections.abc import Mapping


from homeassistant.components.switch import SwitchEntity


from .entity import PoolEquipmentEntity
from .__init__ import NjsPCHAdata
from .const import (
PoolEquipmentClass,
EVENT_SCHEDULE,
EVENT_AVAILABILITY,
API_CONFIG_SCHEDULE,
)

DAY_ABBREVIATIONS = {
"sun": "U",
"sat": "S",
"fri": "F",
"thu": "H",
"wed": "W",
"tue": "T",
"mon": "M",
}


class ScheduleSwitch(PoolEquipmentEntity, SwitchEntity):
"""Schedule switch for njsPC-HA"""

def __init__(
self,
coordinator: NjsPCHAdata,
equipment_class: PoolEquipmentClass,
schedule,
clockMode: int = 12,
body=None,
) -> None:
"""Initialize the switch."""
data = schedule["circuit"]
if body is not None:
# change it over to body if provided
data = body
super().__init__(
coordinator=coordinator,
equipment_class=equipment_class,
data=data,
)

self._available = True
self._value = False
self._clock_mode = clockMode

self._attr_has_entity_name = False
self.schedule_id = schedule["id"]
self.schedule_name = schedule["circuit"]["name"]
if "disabled" in schedule:
self._value = schedule["disabled"]

self._state_attributes: dict[str, Any] = dict([])
if "scheduleDays" in schedule:
self._state_attributes["days"] = self.format_schedule_days(
schedule_days=schedule["scheduleDays"]
)
if "startTime" in schedule and "startTimeType" in schedule:
self._state_attributes["start_time"] = self.format_start_stop_times(
time=schedule["startTime"], time_type=schedule["startTimeType"]["val"]
)
if "endTime" in schedule and "endTimeType" in schedule:
self._state_attributes["end_time"] = self.format_start_stop_times(
time=schedule["endTime"], time_type=schedule["endTimeType"]["val"]
)

def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
if (
self.coordinator.data["event"] == EVENT_SCHEDULE
and self.coordinator.data["id"] == self.schedule_id
):
if "disabled" in self.coordinator.data:
self._value = self.coordinator.data["disabled"]
else:
self._value = False
if (
"circuit" in self.coordinator.data
and "name" in self.coordinator.data["circuit"]
):
self.schedule_name = self.coordinator.data["circuit"]["name"]

if (
"startTime" in self.coordinator.data
and "startTimeType" in self.coordinator.data
):
self._state_attributes["start_time"] = self.format_start_stop_times(
time=self.coordinator.data["startTime"],
time_type=self.coordinator.data["startTimeType"]["val"],
)
if (
"endTime" in self.coordinator.data
and "endTimeType" in self.coordinator.data
):
self._state_attributes["end_time"] = self.format_start_stop_times(
time=self.coordinator.data["endTime"],
time_type=self.coordinator.data["endTimeType"]["val"],
)
if "scheduleDays" in self.coordinator.data:
self._state_attributes["days"] = self.format_schedule_days(
schedule_days=self.coordinator.data["scheduleDays"]
)
self.async_write_ha_state()
elif self.coordinator.data["event"] == EVENT_AVAILABILITY:
self._available = self.coordinator.data["available"]
self.async_write_ha_state()

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
data = {"id": self.schedule_id, "disabled": False}
await self.coordinator.api.command(url=API_CONFIG_SCHEDULE, data=data)

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity on."""
data = {"id": self.schedule_id, "disabled": True}
await self.coordinator.api.command(url=API_CONFIG_SCHEDULE, data=data)

def format_start_stop_times(self, time: int, time_type: int) -> str:
"""Format the minutes time to human readable"""
formatted = ""
if time_type == 1:
return "Sunrise"
elif time_type == 2:
return "Sunset"
elif time_type == 0:
hrs = time // 60
mins = time - (hrs * 60)
if self._clock_mode == 24:
formatted = f"{hrs:02}:{mins:02}"
else:
ampm = "am"
if hrs > 12:
hrs = hrs - 12
ampm = "pm"
formatted = f"{hrs}:{mins:02} {ampm}"
else:
formatted = "Unknown"
return formatted

def format_schedule_days(self, schedule_days) -> str:
"""Format the scheduled days into a string"""
formatted = ""
if schedule_days["val"] == 127:
formatted = "Every Day"
elif schedule_days["val"] == 31:
formatted = "Weekdays"
elif schedule_days["val"] == 96:
formatted = "Weekends"
else:
day_list = []
idx = 0
for day in schedule_days["days"]:
if day["name"] == "sun":
# we need to change the insert index if Sunday is in the list so that it stays at the beginning
idx = 1
day_list.insert(idx, DAY_ABBREVIATIONS[day["name"]])
formatted = "-".join(day_list)
return formatted

@property
def should_poll(self) -> bool:
return False

@property
def available(self) -> bool:
return self._available

@property
def name(self) -> str:
return f"{self.schedule_name} Schedule"

@property
def unique_id(self) -> str:
"""Set unique device_id"""
return f"{self.coordinator.controller_id}_schedule_{self.schedule_id}_disabled"

@property
def is_on(self) -> bool:
return not self._value

@property
def icon(self) -> str:
if self._value:
return "mdi:calendar-remove"
else:
return "mdi:calendar-clock"

@property
def extra_state_attributes(self) -> Mapping[str, Any] | None:
"""Return entity specific state attributes."""

return self._state_attributes
37 changes: 37 additions & 0 deletions custom_components/njspc_ha/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .features import CircuitSwitch
from .chemistry import SuperChlorSwitch
from .bodies import BodyCircuitSwitch
from .schedules import ScheduleSwitch


async def async_setup_entry(
Expand Down Expand Up @@ -85,5 +86,41 @@ async def async_setup_entry(
SuperChlorSwitch(coordinator=coordinator, chlorinator=chlorinator)
)

for schedule in config["schedules"]:
equipment_class = PoolEquipmentClass.AUX_CIRCUIT
_body = None
match schedule["circuit"]["equipmentType"]:
case "circuit":
try:
if schedule["circuit"]["id"] == 1 or schedule["circuit"]["id"] == 6:
for body in config["temps"]["bodies"]:
if (
body["circuit"] == schedule["circuit"]["id"]
and "name" in schedule["circuit"]
):
_body = body
equipment_class = PoolEquipmentClass.BODY
elif schedule["circuit"]["type"]["isLight"]:
equipment_class = PoolEquipmentClass.LIGHT
else:
equipment_class = PoolEquipmentClass.AUX_CIRCUIT
except KeyError:
equipment_class = PoolEquipmentClass.AUX_CIRCUIT
case "circuitGroup":
equipment_class = PoolEquipmentClass.CIRCUIT_GROUP
case "feature":
equipment_class = PoolEquipmentClass.FEATURE
case "lightGroup":
equipment_class = PoolEquipmentClass.LIGHT_GROUP
new_devices.append(
ScheduleSwitch(
coordinator=coordinator,
equipment_class=equipment_class,
schedule=schedule,
body=_body,
clockMode=config["clockMode"]["val"]
)
)

if new_devices:
async_add_entities(new_devices)

0 comments on commit b5f3e85

Please sign in to comment.