Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit 44d796d

Browse files
authored
Add switches for charger settings (#6)
* Add settings switches * Add capability check * Doc updates * Doc updates
1 parent 6fad42c commit 44d796d

File tree

8 files changed

+164
-41
lines changed

8 files changed

+164
-41
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ This is an unofficial integration. I have no affiliation with Ohme besides ownin
66

77
This has only be tested with an Ohme Home Pro and does not currently support social login or accounts with multiple chargers.
88

9-
It's still very early in development but I plan to add more sensors and support for pausing/resuming charge.
10-
119
## Entities
1210
This integration exposes the following entities:
1311

@@ -18,7 +16,11 @@ This integration exposes the following entities:
1816
* Power Draw (Watts) - Power draw of connected car
1917
* Accumulative Energy Usage (kWh) - Total energy used by the charger
2018
* Next Smart Charge Slot - The next time your car will start charging according to the Ohme-generated charge plan
21-
* Switches - These are only functional when a car is connected
19+
* Switches (Settings) - Only options available to your charger model will show
20+
* Lock Buttons - Locks buttons on charger
21+
* Require Approval - Require approval to start a charge
22+
* Sleep When Inactive - Charger screen & lights will automatically turn off
23+
* Switches (Charge state) - These are only functional when a car is connected
2224
* Max Charge - Forces the connected car to charge regardless of set schedule
2325
* Pause Charge - Pauses an ongoing charge
2426

@@ -36,4 +38,4 @@ This is the recommended installation method.
3638
3. Restart Home Assistant
3739

3840
## Setup
39-
From the Home Assistant Integrations page, search for an add the Ohme integration. If you created your Ohme account through a social login, you will need to 'reset your password' to use this integration.
41+
From the Home Assistant Integrations page, search for an add the Ohme integration. If you created your Ohme account through a social login, you will need to 'reset your password' to use this integration.

custom_components/ohme/__init__.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from homeassistant import core
22
from .const import *
33
from .api_client import OhmeApiClient
4-
from .coordinator import OhmeUpdateCoordinator, OhmeStatisticsUpdateCoordinator
4+
from .coordinator import OhmeChargeSessionsCoordinator, OhmeStatisticsCoordinator, OhmeAccountInfoCoordinator
55

66

77
async def async_setup(hass: core.HomeAssistant, config: dict) -> bool:
@@ -31,13 +31,17 @@ async def async_setup_entry(hass, entry):
3131

3232
await async_setup_dependencies(hass, config)
3333

34-
hass.data[DOMAIN][DATA_COORDINATOR] = OhmeUpdateCoordinator(hass=hass)
35-
await hass.data[DOMAIN][DATA_COORDINATOR].async_config_entry_first_refresh()
34+
hass.data[DOMAIN][DATA_CHARGESESSIONS_COORDINATOR] = OhmeChargeSessionsCoordinator(hass=hass)
35+
await hass.data[DOMAIN][DATA_CHARGESESSIONS_COORDINATOR].async_config_entry_first_refresh()
3636

37-
hass.data[DOMAIN][DATA_STATISTICS_COORDINATOR] = OhmeStatisticsUpdateCoordinator(
37+
hass.data[DOMAIN][DATA_STATISTICS_COORDINATOR] = OhmeStatisticsCoordinator(
3838
hass=hass)
3939
await hass.data[DOMAIN][DATA_STATISTICS_COORDINATOR].async_config_entry_first_refresh()
4040

41+
hass.data[DOMAIN][DATA_ACCOUNTINFO_COORDINATOR] = OhmeAccountInfoCoordinator(
42+
hass=hass)
43+
await hass.data[DOMAIN][DATA_ACCOUNTINFO_COORDINATOR].async_config_entry_first_refresh()
44+
4145
# Create tasks for each entity type
4246
hass.async_create_task(
4347
hass.config_entries.async_forward_entry_setup(entry, "sensor")

custom_components/ohme/api_client.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import aiohttp
22
import logging
3+
import json
34
from datetime import datetime, timedelta
45
from homeassistant.helpers.entity import DeviceInfo
56
from .const import DOMAIN
@@ -18,6 +19,7 @@ def __init__(self, email, password):
1819
self._password = password
1920

2021
self._device_info = None
22+
self._capabilities = {}
2123
self._token = None
2224
self._user_id = ""
2325
self._serial = ""
@@ -63,8 +65,11 @@ async def _put_request(self, url, data=None, is_retry=False):
6365
If we get a non 200 response, refresh auth token and try again"""
6466
async with self._session.put(
6567
url,
66-
data=data,
67-
headers={"Authorization": "Firebase %s" % self._token}
68+
data=json.dumps(data),
69+
headers={
70+
"Authorization": "Firebase %s" % self._token,
71+
"Content-Type": "application/json"
72+
}
6873
) as resp:
6974
if resp.status != 200 and not is_retry:
7075
await self.async_refresh_session()
@@ -110,6 +115,11 @@ async def async_stop_max_charge(self):
110115
result = await self._put_request(f"https://api.ohme.io/v1/chargeSessions/{self._serial}/rule?enableMaxPrice=false&toPercent=80.0&inSeconds=43200")
111116
return bool(result)
112117

118+
async def async_set_configuration_value(self, values):
119+
"""Set a configuration value or values."""
120+
result = await self._put_request(f"https://api.ohme.io/v1/chargeDevices/{self._serial}/appSettings", data=values)
121+
return bool(result)
122+
113123
async def async_get_charge_sessions(self, is_retry=False):
114124
"""Try to fetch charge sessions endpoint.
115125
If we get a non 200 response, refresh auth token and try again"""
@@ -120,9 +130,17 @@ async def async_get_charge_sessions(self, is_retry=False):
120130

121131
return resp[0]
122132

133+
async def async_get_account_info(self):
134+
resp = await self._get_request('https://api.ohme.io/v1/users/me/account')
135+
136+
if not resp:
137+
return False
138+
139+
return resp
140+
123141
async def async_update_device_info(self, is_retry=False):
124142
"""Update _device_info with our charger model."""
125-
resp = await self._get_request('https://api.ohme.io/v1/users/me/account')
143+
resp = await self.async_get_account_info()
126144

127145
if not resp:
128146
return False
@@ -138,12 +156,17 @@ async def async_update_device_info(self, is_retry=False):
138156
serial_number=device['id']
139157
)
140158

159+
self._capabilities = device['modelCapabilities']
141160
self._user_id = resp['user']['id']
142161
self._serial = device['id']
143162
self._device_info = info
144163

145164
return True
146165

166+
def is_capable(self, capability):
167+
"""Return whether or not this model has a given capability."""
168+
return bool(self._capabilities[capability])
169+
147170
def _last_second_of_month_timestamp(self):
148171
"""Get the last second of this month."""
149172
dt = datetime.today()

custom_components/ohme/binary_sensor.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
from homeassistant.helpers.update_coordinator import CoordinatorEntity
99
from homeassistant.core import HomeAssistant
1010
from homeassistant.helpers.entity import generate_entity_id
11-
from .const import DOMAIN, DATA_COORDINATOR, DATA_CLIENT
12-
from .coordinator import OhmeUpdateCoordinator
11+
from .const import DOMAIN, DATA_CHARGESESSIONS_COORDINATOR, DATA_CLIENT
12+
from .coordinator import OhmeChargeSessionsCoordinator
1313

1414

1515
async def async_setup_entry(
@@ -19,7 +19,7 @@ async def async_setup_entry(
1919
):
2020
"""Setup sensors and configure coordinator."""
2121
client = hass.data[DOMAIN][DATA_CLIENT]
22-
coordinator = hass.data[DOMAIN][DATA_COORDINATOR]
22+
coordinator = hass.data[DOMAIN][DATA_CHARGESESSIONS_COORDINATOR]
2323

2424
sensors = [ConnectedSensor(coordinator, hass, client),
2525
ChargingSensor(coordinator, hass, client)]
@@ -28,7 +28,7 @@ async def async_setup_entry(
2828

2929

3030
class ConnectedSensor(
31-
CoordinatorEntity[OhmeUpdateCoordinator],
31+
CoordinatorEntity[OhmeChargeSessionsCoordinator],
3232
BinarySensorEntity):
3333
"""Binary sensor for if car is plugged in."""
3434

@@ -37,7 +37,7 @@ class ConnectedSensor(
3737

3838
def __init__(
3939
self,
40-
coordinator: OhmeUpdateCoordinator,
40+
coordinator: OhmeChargeSessionsCoordinator,
4141
hass: HomeAssistant,
4242
client):
4343
super().__init__(coordinator=coordinator)
@@ -74,7 +74,7 @@ def is_on(self) -> bool:
7474

7575

7676
class ChargingSensor(
77-
CoordinatorEntity[OhmeUpdateCoordinator],
77+
CoordinatorEntity[OhmeChargeSessionsCoordinator],
7878
BinarySensorEntity):
7979
"""Binary sensor for if car is charging."""
8080

@@ -83,7 +83,7 @@ class ChargingSensor(
8383

8484
def __init__(
8585
self,
86-
coordinator: OhmeUpdateCoordinator,
86+
coordinator: OhmeChargeSessionsCoordinator,
8787
hass: HomeAssistant,
8888
client):
8989
super().__init__(coordinator=coordinator)

custom_components/ohme/const.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
DOMAIN = "ohme"
44
DATA_CLIENT = "client"
5-
DATA_COORDINATOR = "coordinator"
5+
DATA_CHARGESESSIONS_COORDINATOR = "coordinator"
66
DATA_STATISTICS_COORDINATOR = "statistics_coordinator"
7+
DATA_ACCOUNTINFO_COORDINATOR = "accountinfo_coordinator"

custom_components/ohme/coordinator.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
_LOGGER = logging.getLogger(__name__)
1212

1313

14-
class OhmeUpdateCoordinator(DataUpdateCoordinator):
14+
class OhmeChargeSessionsCoordinator(DataUpdateCoordinator):
1515
"""Coordinator to pull from API periodically."""
1616

1717
def __init__(self, hass):
@@ -32,8 +32,29 @@ async def _async_update_data(self):
3232
except BaseException:
3333
raise UpdateFailed("Error communicating with API")
3434

35+
class OhmeAccountInfoCoordinator(DataUpdateCoordinator):
36+
"""Coordinator to pull from API periodically."""
37+
38+
def __init__(self, hass):
39+
"""Initialise coordinator."""
40+
super().__init__(
41+
hass,
42+
_LOGGER,
43+
name="Ohme Account Info",
44+
update_interval=timedelta(minutes=1),
45+
)
46+
self._client = hass.data[DOMAIN][DATA_CLIENT]
47+
48+
async def _async_update_data(self):
49+
"""Fetch data from API endpoint."""
50+
try:
51+
return await self._client.async_get_account_info()
52+
53+
except BaseException:
54+
raise UpdateFailed("Error communicating with API")
55+
3556

36-
class OhmeStatisticsUpdateCoordinator(DataUpdateCoordinator):
57+
class OhmeStatisticsCoordinator(DataUpdateCoordinator):
3758
"""Coordinator to update statistics from API periodically.
3859
(But less so than OhmeUpdateCoordinator)"""
3960

custom_components/ohme/sensor.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
from homeassistant.core import HomeAssistant, callback
1111
from homeassistant.helpers.entity import generate_entity_id
1212
from homeassistant.util.dt import (utcnow)
13-
from .const import DOMAIN, DATA_CLIENT, DATA_COORDINATOR, DATA_STATISTICS_COORDINATOR
14-
from .coordinator import OhmeUpdateCoordinator, OhmeStatisticsUpdateCoordinator
13+
from .const import DOMAIN, DATA_CLIENT, DATA_CHARGESESSIONS_COORDINATOR, DATA_STATISTICS_COORDINATOR
14+
from .coordinator import OhmeChargeSessionsCoordinator, OhmeStatisticsCoordinator
1515
from .utils import charge_graph_next_slot
1616

1717

@@ -22,7 +22,7 @@ async def async_setup_entry(
2222
):
2323
"""Setup sensors and configure coordinator."""
2424
client = hass.data[DOMAIN][DATA_CLIENT]
25-
coordinator = hass.data[DOMAIN][DATA_COORDINATOR]
25+
coordinator = hass.data[DOMAIN][DATA_CHARGESESSIONS_COORDINATOR]
2626
stats_coordinator = hass.data[DOMAIN][DATA_STATISTICS_COORDINATOR]
2727

2828
sensors = [PowerDrawSensor(coordinator, hass, client), EnergyUsageSensor(
@@ -31,15 +31,15 @@ async def async_setup_entry(
3131
async_add_entities(sensors, update_before_add=True)
3232

3333

34-
class PowerDrawSensor(CoordinatorEntity[OhmeUpdateCoordinator], SensorEntity):
34+
class PowerDrawSensor(CoordinatorEntity[OhmeChargeSessionsCoordinator], SensorEntity):
3535
"""Sensor for car power draw."""
3636
_attr_name = "Current Power Draw"
3737
_attr_native_unit_of_measurement = UnitOfPower.WATT
3838
_attr_device_class = SensorDeviceClass.POWER
3939

4040
def __init__(
4141
self,
42-
coordinator: OhmeUpdateCoordinator,
42+
coordinator: OhmeChargeSessionsCoordinator,
4343
hass: HomeAssistant,
4444
client):
4545
super().__init__(coordinator=coordinator)
@@ -73,15 +73,15 @@ def native_value(self):
7373
return 0
7474

7575

76-
class EnergyUsageSensor(CoordinatorEntity[OhmeStatisticsUpdateCoordinator], SensorEntity):
76+
class EnergyUsageSensor(CoordinatorEntity[OhmeStatisticsCoordinator], SensorEntity):
7777
"""Sensor for total energy usage."""
7878
_attr_name = "Accumulative Energy Usage"
7979
_attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
8080
_attr_device_class = SensorDeviceClass.ENERGY
8181

8282
def __init__(
8383
self,
84-
coordinator: OhmeUpdateCoordinator,
84+
coordinator: OhmeChargeSessionsCoordinator,
8585
hass: HomeAssistant,
8686
client):
8787
super().__init__(coordinator=coordinator)
@@ -116,14 +116,14 @@ def native_value(self):
116116
return None
117117

118118

119-
class NextSlotSensor(CoordinatorEntity[OhmeStatisticsUpdateCoordinator], SensorEntity):
119+
class NextSlotSensor(CoordinatorEntity[OhmeStatisticsCoordinator], SensorEntity):
120120
"""Sensor for next smart charge slot."""
121121
_attr_name = "Next Smart Charge Slot"
122122
_attr_device_class = SensorDeviceClass.TIMESTAMP
123123

124124
def __init__(
125125
self,
126-
coordinator: OhmeUpdateCoordinator,
126+
coordinator: OhmeChargeSessionsCoordinator,
127127
hass: HomeAssistant,
128128
client):
129129
super().__init__(coordinator=coordinator)

0 commit comments

Comments
 (0)