Skip to content

Commit

Permalink
Restore service to 3.0.0. Fixes #135
Browse files Browse the repository at this point in the history
  • Loading branch information
sebr committed May 29, 2022
1 parent e2f213a commit eb45b30
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 4 deletions.
34 changes: 34 additions & 0 deletions custom_components/bhyve/pybhyve/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
API_POLL_PERIOD,
DEVICE_HISTORY_PATH,
DEVICES_PATH,
LANDSCAPE_DESCRIPTIONS_PATH,
LOGIN_PATH,
TIMER_PROGRAMS_PATH,
WS_HOST,
Expand Down Expand Up @@ -45,6 +46,9 @@ def __init__(self, username: str, password: str, session) -> None:
self._device_histories = {}
self._last_poll_device_histories = 0

self._landscapes = list[Any]
self._last_poll_landscapes = 0

async def _request(
self, method: str, endpoint: str, params: dict = None, json: dict = None
) -> list:
Expand Down Expand Up @@ -121,6 +125,21 @@ async def _refresh_device_history(self, device_id, force_update=False):

self._last_poll_device_histories = now

async def _refresh_landscapes(self, device_id, force_update=False):
now = time.time()
if force_update:
_LOGGER.debug("Forcing landscape refresh %s", device_id)
elif now - self._last_poll_landscapes < API_POLL_PERIOD:
return

self._landscapes = await self._request(
"get",
f"{LANDSCAPE_DESCRIPTIONS_PATH}/{device_id}",
params={"t": str(time.time())},
)

self._last_poll_landscapes = now

async def _async_ws_handler(self, async_callback, data):
"""Process incoming websocket message."""
ensure_future(async_callback(data))
Expand Down Expand Up @@ -193,6 +212,21 @@ async def get_device_history(self, device_id, force_update=False):
await self._refresh_device_history(device_id, force_update=force_update)
return self._device_histories.get(device_id)

async def get_landscape(self, device_id, zone_id, force_update=False):
"""Get landscape by zone id."""
await self._refresh_landscapes(device_id, force_update)
for zone in self._landscapes:
if zone.get("station") == zone_id:
return zone
return None

async def update_landscape(self, landscape):
"""Update the state of a zone landscape."""
landscape_id = landscape.get("id")
path = f"{LANDSCAPE_DESCRIPTIONS_PATH}/{landscape_id}"
json = {"landscape_description": landscape}
await self._request("put", path, json=json)

async def update_program(self, program_id, program):
"""Update the state of a program."""
path = f"{TIMER_PROGRAMS_PATH}/{program_id}"
Expand Down
10 changes: 10 additions & 0 deletions custom_components/bhyve/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,13 @@ set_manual_preset_runtime:
minutes:
description: Number of minutes to set the preset runtime
example: 15

set_smart_watering_soil_moisture:
description: Set the smart watering soil moisture level for a zone
fields:
entity_id:
description: Zone
example: "switch.backyard_zone"
percentage:
description: Moisture level between 0 - 100 (percent)
example: 50
77 changes: 73 additions & 4 deletions custom_components/bhyve/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
# Service Attributes
ATTR_MINUTES = "minutes"
ATTR_HOURS = "hours"
ATTR_PERCENTAGE = "percentage"

# Rain Delay Attributes
ATTR_CAUSE = "cause"
Expand Down Expand Up @@ -88,11 +89,19 @@
}
)

SET_SMART_WATERING_SOIL_MOISTURE_SCHEMA = SERVICE_BASE_SCHEMA.extend(
{
vol.Required(ATTR_PERCENTAGE): cv.positive_int,
}
)

SERVICE_ENABLE_RAIN_DELAY = "enable_rain_delay"
SERVICE_DISABLE_RAIN_DELAY = "disable_rain_delay"
SERVICE_START_WATERING = "start_watering"
SERVICE_STOP_WATERING = "stop_watering"
SERVICE_SET_MANUAL_PRESET_RUNTIME = "set_manual_preset_runtime"
SERVICE_SET_SMART_WATERING_SOIL_MOISTURE = "set_smart_watering_soil_moisture"


SERVICE_TO_METHOD = {
SERVICE_ENABLE_RAIN_DELAY: {
Expand All @@ -112,6 +121,10 @@
"method": "set_manual_preset_runtime",
"schema": SET_PRESET_RUNTIME_SCHEMA,
},
SERVICE_SET_SMART_WATERING_SOIL_MOISTURE: {
"method": "set_smart_watering_soil_moisture",
"schema": SET_SMART_WATERING_SOIL_MOISTURE_SCHEMA,
},
}


Expand Down Expand Up @@ -316,6 +329,7 @@ def __init__(self, hass, bhyve, device, zone, device_programs, icon):
self._zone_id = zone.get("station")
self._entity_picture = zone.get("image_url")
self._zone_name = zone.get("name")
self._smart_watering_enabled = zone.get("smart_watering_enabled")
self._manual_preset_runtime = device.get(
"manual_preset_runtime_sec", DEFAULT_MANUAL_RUNTIME.seconds
)
Expand All @@ -333,7 +347,7 @@ def _setup(self, device):
"device_name": self._device_name,
"device_id": self._device_id,
"zone_name": self._zone_name,
ATTR_SMART_WATERING_ENABLED: False,
ATTR_SMART_WATERING_ENABLED: self._smart_watering_enabled,
}
self._available = device.get("is_connected", False)

Expand Down Expand Up @@ -407,9 +421,6 @@ def _set_watering_program(self, program):
"is_smart_program": is_smart_program,
}

if is_smart_program:
self._attrs[ATTR_SMART_WATERING_ENABLED] = program_enabled

if not program_enabled or not active_program_run_times:
_LOGGER.info(
"%s Zone: Watering program %s (%s) is not enabled, skipping",
Expand Down Expand Up @@ -534,6 +545,64 @@ def is_on(self):
"""Return the status of the sensor."""
return self._is_on

async def set_smart_watering_soil_moisture(self, percentage):
"""Set the soil moisture percentage for the zone."""
if self._smart_watering_enabled:
landscape = None
try:
landscape = await self._bhyve.get_landscape(
self._device_id, self._zone_id
)

except BHyveError as err:
_LOGGER.warning(
"Unable to retreive current soil data for %s: %s", self.name, err
)

if landscape is not None:
_LOGGER.debug("Landscape data %s", landscape)

# Define the minimum landscape update json payload
landscape_update = {
"current_water_level": 0,
"device_id": "",
"id": "",
"station": 0,
}

# B-hyve computed value for 0% moisture
landscape_moisture_level_0 = landscape["replenishment_point"]

# B-hyve computed value for 100% moisture
landscape_moisture_level_100 = landscape["field_capacity_depth"]

# Set property to computed user desired soil moisture level
landscape_update["current_water_level"] = landscape_moisture_level_0 + (
(
percentage
* (landscape_moisture_level_100 - landscape_moisture_level_0)
)
/ 100.0
)
# Set remaining properties
landscape_update["device_id"] = self._device_id
landscape_update["id"] = landscape["id"]
landscape_update["station"] = self._zone_id

try:
_LOGGER.debug("Landscape update %s", landscape_update)
await self._bhyve.update_landscape(landscape_update)

except BHyveError as err:
_LOGGER.warning(
"Unable to set soil moisture level for %s: %s", self.name, err
)
else:
_LOGGER.info(
"Zone %s isn't smart watering enabled, cannot set soil moisture.",
self._zone_name,
)

async def start_watering(self, minutes):
"""Turns on the switch and starts watering."""
station_payload = [{"station": self._zone_id, "run_time": minutes}]
Expand Down

0 comments on commit eb45b30

Please sign in to comment.