Open pmcgaley opened 3 years ago
thanks for the heads up. I'll see if I can get some time to investigate the new API and update
It seems the protocol has slightly changed and that has broken things.
in get_home() it drops out with error 32: {'timestamp': 1612988335736, 'status': 32, 'data': None, 'message': 'WiFi device is not online, please check and try again.'}
COMMENT When I have the phone app running and run the python API I do not get this error but it fails then later in another call.
It seems that there is an attempt to exclude anything that is not a phone app from calling the API. COMMENT
I cannot replicate the above behaviour anymore. It now drops out with this error 32 regardless if I interact with the phone app at the same time.
Thanks. I've just started to look at the new API and will hopefully have an update soon.
See this issue in home assistant for some more discussion recent API issues
In
def get_home(self, gateway_id=None):
the url, headers and data submitted in the request are:
https://eu-https.topband-cloud.com/ember-back/zones/polling
{'Authorization': 'XXXXXXXXXXXXXXXXXXXXX', 'Accept': 'application/json', 'Content-Type': 'application/json'}
{'gateWayId': 'XXXXXXXXX'}
the response is <Response [200]>
And response.json()
looks like this:
{'status': 32, 'message': 'WiFi device is not online, please check and try again.', 'timestamp': 1613218949350, 'data': None}
And it fails with:
Traceback (most recent call last):
File "openhabEMBER.py", line 122, in
I've pushed https://github.com/ttroy50/pyephember/blob/update_api_docs/API.md which has some of the work from my documentation of the new API. unfortunately this is from a couple of weeks ago and I haven't had a chance to expand on it yet. I'm getting very little time to look into this at the moment.
Hi @ttroy50, I'd like to help out and contribute to this, but I'm having difficulty capturing the traffic. How are you doing it? I used polarproxy, but it's not able to establish a connection to the mqtt server, so I'm not getting a pcap output for this traffic. It's probably a local issue though.
I was able to modify mitmproxy-mqtt-script to give me hex output, but I see your output looks like you're opening it in WireShark.
I did find that they might be using EnOS (https://github.com/EnvisionIot) as their IoT manager. It uses port 18883 for MQTTS, when the standard is 8883. So once we can figure out the rest of the parameters used by Ember, we might be able to use that.
Curious if this still works Been reading the Api doc that @ttroy50 updated, i can get a token but no other call passes Always get { "message": "Not logged in or session timeout.", "status": 9, "timestamp": 1638530699855 }
Im using the following auth format for auth "Authorization: Bearer XXXX" -> where XXXX = "data.token" from login It might be related to the reportToken step but ive no idea what "really_long_phone_token" is representing (i've tried unique UUID 4 without much luck)
"phoneToken": "really_long_phone_token"
I've also noticed its HTTP 2.0 - something postman doesn't yet support, is the a hard requirement?
Curious how others are getting on.
@DarkSlice1 you are passing the auth token slightly incorrectly, and the error message is very opaque.
It's just
Authorization: token
You just need to drop the bearer, since they are using a custom auth mechanism.
ah perfect, thank you 👍
Point index 10, if value = 2, then the boiler is active and burning oil
The code in pull request #13 is working for me with the current API.
The code in pull request 13 works for me (I was looking for get_zone_status() which is now is_zone_boiler_on() but it all seems to work. Thanks.
If anyone else here could test the code from https://github.com/ttroy50/pyephember/pull/13 and let us know if it works it would be very helpful towards getting a new release out.
Thanks to @rupertleveneucd for doing the change.
Hi @ttroy50, I've tested it with the latest version from master
and all works great. Would be super to get a release out and into home assistant - anything I could do there to help with that?
I've tested latest version from master and working for me well. How can we go about getting ephember home assistant updated with this method.
@ttroy50 I think all that is needed is to bump this master version on pypi.org. The EPHcontrol in home assistant already pull this as a requirement in it's manifest file. It's currently pulling version 3.9. So we can clone that to a custom_component and test there.
Is it not version 0.3.1? The manifest looks for 'pyephember 0.3.1'? Where do you get the 3.9 version number (Maybe I look at the wrong place). Is there a way to place the correct library with a custom_component to bypass the update on pypi.org? I am happy to test.
You are correct 0.3.1 apologies. I have my own custom component but the Requirements paramater in the manifest.json no longer allow you to specify a GIT. Has to come from pypi.https://www.google.com/url?sa=t&source=web&rct=j&url=https://developers.home-assistant.io/docs/creating_component_code_review/&ved=2ahUKEwjhjPbOwZD8AhVTZ8AKHUxvDbYQFnoECA0QAQ&usg=AOvVaw0yDfe2nFRX8nPONLZZQ3wc
Not yet familiar with home assistant, currently try to switch from openhab to that. Is it not possible to have the library directly within the custom_component and not use the requirements mechanism to retrieve that from pypi.org. Ideally an update in pypi.org is better but as a solution in the meantime.
ok I was able to do a pip install in the home assistant container directly. The method from here https://developers.home-assistant.io/docs/creating_integration_manifest/
Then the custom component loaded but threw some errors around a is_hot_water so I removed all entries around that. Reloaded and now I can see and add my zones in HA. Very nice. Most of the calls don't work but its a good step forward !
Some of the api has changed. For example get_zone_status() which is now is_zone_boiler_on() and the is_hot_water does not exist anymore; so some adjustment might be needed to get everything working.
Not sure how you managed to install this, I need to spend some time to figure out how to do the pip install on a docker container I am running for this ...
add the standard install to you config file. climate:
Create a folder under custom_componets called ephember. Needs to be the same name to override the standard component. Copy in 3 files from the main source component. https://github.com/home-assistant/core/tree/dev/homeassistant/components/ephember
Remove the version from the requirement in manifest.json
Get to the homeassistant container console. In my case through portioner as I have HASSOS
Git Clone https://github.com/ttroy50/pyephember.git
Pip install -e ./pyephember
Restart HA and check the log file of errors. Remove lines from climate.py referring stuff that doesn't exist anymore. Restart once more and the integration is loaded. can add my zones and most of the gets work.
Would need someone who understands the API to adjust for the changes since the last time this was working was 2 years ago.
It took me a while. I have a docker container, so no ssh and no shell; needed to learn you can use 'docker exec' to execute commands in the container and then did the above. Run into some issues.
I did not understand what you mean exactly by 'Remove the version from the requirement in manifest.json'. So instead I set the version to 0.4.0 which is the new installed pyephember.
I restarted home assistant and got these two errors:
Platform error: climate - Requirements for ephember not found: ['pyephember==0.3.1'].
22:31:13 – (ERROR) config.py
Unable to install package pyephember==0.3.1: ERROR: Could not install packages due to an OSError: [Errno 13] Permission denied: '/.local' Check the permissions. [notice] A new release of pip available: 22.3 -> 22.3.1 [notice] To update, run: pip install --upgrade pip
22:31:13 – (ERROR) util/package.py - message first occurred at 22:30:33 and shows up 3 times
So it seems it wants to install the old version again, how do you prevent this?
---> Found out the manifest.json needs a version number otherwise home assistant takes the original version and not the new one in custom_components
I think your custom component isn't getting picked up. Hence the default component is complaining. Did you add a version param to the manifest file ?
On Fri 23 Dec 2022, 22:42 UtzR, @.***> wrote:
It took me a while. I have a docker container, so no ssh and no shell; needed to learn you can use 'docker exec' to execute commands in the container and then did the above. Run into some issues.
I did not understand what you mean exactly by 'Remove the version from the requirement in manifest.json'. So instead I set the version to 0.4.0 which is the new installed pyephember.
I restarted home assistant and got these two errors:
Platform error: climate - Requirements for ephember not found: ['pyephember==0.3.1']. 22:31:13 – (ERROR) config.py
Unable to install package pyephember==0.3.1: ERROR: Could not install packages due to an OSError: [Errno 13] Permission denied: '/.local' Check the permissions. [notice] A new release of pip available: 22.3 -> 22.3.1 [notice] To update, run: pip install --upgrade pip 22:31:13 – (ERROR) util/package.py - message first occurred at 22:30:33 and shows up 3 times
So it seems it wants to install the old version again, how do you prevent this?
— Reply to this email directly, view it on GitHub https://github.com/ttroy50/pyephember/issues/11#issuecomment-1364370785, or unsubscribe https://github.com/notifications/unsubscribe-auth/ALVJ7EO3W7OLDP64ECAKPCLWOYTGVANCNFSM4VOKRRFA . You are receiving this because you commented.Message ID: @.***>
OK some more time to play with it today so I have fixed up the boost (aux), set_temperature and set_mode functions. I had to remove the Hot Water zone detection from the old methods as I'm not so good to at coding to figure out how that works.I think it is device_type, Basically if it is a hot water zone the temperature set isn't supposed to be set. Be careful with that hopefully someone with more knowledge can make it work like it use to. I set the max temp to be 50 so I can control the hot water.
Original code: https://github.com/home-assistant/core/blob/dev/homeassistant/components/ephember/climate.py
"""Support for the EPH Controls Ember themostats."""
from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
from pyephember.pyephember import (
EphEmber,
ZoneMode,
zone_current_temperature,
zone_is_active,
zone_is_boost_active,
zone_boost_hours,
zone_boost_timestamp,
zone_advance_active,
boiler_state,
zone_is_scheduled_on,
zone_mode,
zone_name,
zone_temperature,
zone_target_temperature,
zone_boost_temperature,
zone_current_temperature,
zone_pointdata_value,
)
import voluptuous as vol
from homeassistant.components.climate import (
PLATFORM_SCHEMA,
ClimateEntity,
ClimateEntityFeature,
HVACAction,
HVACMode,
)
from homeassistant.const import (
ATTR_TEMPERATURE,
CONF_PASSWORD,
CONF_USERNAME,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
_LOGGER = logging.getLogger(__name__)
# Return cached results if last scan was less then this time ago
SCAN_INTERVAL = timedelta(seconds=10)
OPERATION_LIST = [HVACMode.HEAT_COOL, HVACMode.HEAT, HVACMode.OFF]
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string}
)
EPH_TO_HA_STATE = {
"AUTO": HVACMode.HEAT_COOL,
"ON": HVACMode.HEAT,
"OFF": HVACMode.OFF,
}
HA_STATE_TO_EPH = {value: key for key, value in EPH_TO_HA_STATE.items()}
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the ephember thermostat."""
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
try:
ember = EphEmber(username, password)
zones = ember.get_zones()
for zone in zones:
add_entities([EphEmberThermostat(ember, zone)])
except RuntimeError:
_LOGGER.error("Cannot connect to EphEmber")
return
return
class EphEmberThermostat(ClimateEntity):
"""Representation of a EphEmber thermostat."""
_attr_hvac_modes = OPERATION_LIST
_attr_temperature_unit = UnitOfTemperature.CELSIUS
def __init__(self, ember, zone):
"""Initialize the thermostat."""
self._ember = ember
self._zone_name = zone_name(zone)
self._zone = zone
self._attr_name = self._zone_name
self._attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.AUX_HEAT
)
self._attr_target_temperature_step = 0.5
@property
def current_temperature(self):
"""Return the current temperature."""
return zone_current_temperature(self._zone)
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return zone_target_temperature(self._zone)
@property
def hvac_action(self) -> HVACAction:
"""Return current HVAC action."""
if zone_is_active(self._zone):
return HVACAction.HEATING
return HVACAction.IDLE
@property
def hvac_mode(self) -> HVACMode:
"""Return current operation ie. heat, cool, idle."""
mode = zone_mode(self._zone)
return self.map_mode_eph_hass(mode)
def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the operation mode."""
mode = self.map_mode_hass_eph(hvac_mode)
if mode is not None:
self._ember.set_zone_mode(self._zone_name, mode)
else:
_LOGGER.error("Invalid operation mode provided %s", hvac_mode)
@property
def is_aux_heat(self):
"""Return true if aux heater."""
return zone_is_boost_active(self._zone)
def turn_aux_heat_on(self) -> None:
"""Turn auxiliary heater on."""
self._ember.activate_zone_boost(
self._zone_name, zone_target_temperature(self._zone)
)
def turn_aux_heat_off(self) -> None:
"""Turn auxiliary heater off."""
self._ember.deactivate_zone_boost(self._zone_name)
def set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
return
if temperature == self.target_temperature:
return
if temperature > self.max_temp or temperature < self.min_temp:
return
self._ember.set_zone_target_temperature(self._zone_name, temperature)
@property
def min_temp(self):
"""Return the minimum temperature."""
# Hot water temp doesn't support being changed
return 5.0
@property
def max_temp(self):
"""Return the maximum temperature."""
return 50.0
def update(self) -> None:
"""Get the latest data."""
self._zone = self._ember.get_zone(self._zone_name)
@staticmethod
def map_mode_hass_eph(operation_mode):
"""Map from Home Assistant mode to eph mode."""
return getattr(ZoneMode, HA_STATE_TO_EPH.get(operation_mode), None)
@staticmethod
def map_mode_eph_hass(operation_mode):
"""Map from eph mode to Home Assistant mode."""
return EPH_TO_HA_STATE.get(operation_mode.name, HVACMode.HEAT_COOL)
just tested your code and that works. Thanks. I will experiment with it for a bit more ...
@ttroy50 could you bump the Master 4.0 version to the latest on PyPi ? https://pypi.org/project/pyephember/
I have device type 4 on my immersion sensor and 2 on my normal rooms if that's any help. i have supplied my version of the code which keeps the hot water code but disables the detection. if we know the device type for that it could be simply updated. i added support for immersions which have a temp limit of 90. (at least on the controller)
"""Support for the EPH Controls Ember themostats."""
from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
from pyephember.pyephember import (
EphEmber,
ZoneMode,
zone_current_temperature,
zone_is_active,
zone_is_boost_active,
zone_mode,
zone_name,
zone_target_temperature
)
import voluptuous as vol
from homeassistant.components.climate import (
PLATFORM_SCHEMA,
ClimateEntity,
ClimateEntityFeature,
HVACAction,
HVACMode,
)
from homeassistant.const import (
ATTR_TEMPERATURE,
CONF_PASSWORD,
CONF_USERNAME,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
_LOGGER = logging.getLogger(__name__)
# Return cached results if last scan was less then this time ago
SCAN_INTERVAL = timedelta(seconds=120)
OPERATION_LIST = [HVACMode.HEAT_COOL, HVACMode.HEAT, HVACMode.OFF]
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string}
)
EPH_TO_HA_STATE = {
"AUTO": HVACMode.HEAT_COOL,
"ON": HVACMode.HEAT,
"OFF": HVACMode.OFF,
}
HA_STATE_TO_EPH = {value: key for key, value in EPH_TO_HA_STATE.items()}
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the ephember thermostat."""
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
try:
ember = EphEmber(username, password)
zones = ember.get_zones()
for zone in zones:
add_entities([EphEmberThermostat(ember, zone)])
except RuntimeError as e:
_LOGGER.error("Cannot connect to EphEmber")
return
return
class EphEmberThermostat(ClimateEntity):
"""Representation of a EphEmber thermostat."""
_attr_hvac_modes = OPERATION_LIST
_attr_temperature_unit = UnitOfTemperature.CELSIUS
def __init__(self, ember, zone):
"""Initialize the thermostat."""
self._ember = ember
self._zone_name = zone_name(zone)
self._zone = zone
self._hot_water = False
# self._immersion = False
self._immersion = zone['deviceType'] == 4
self._attr_name = self._zone_name
self._attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.AUX_HEAT
)
self._attr_target_temperature_step = 0.5
if self._hot_water:
self._attr_supported_features = ClimateEntityFeature.AUX_HEAT
self._attr_target_temperature_step = None
@property
def current_temperature(self):
"""Return the current temperature."""
return zone_current_temperature(self._zone)
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return zone_target_temperature(self._zone)
@property
def hvac_action(self) -> HVACAction:
"""Return current HVAC action."""
if zone_is_active(self._zone):
return HVACAction.HEATING
return HVACAction.IDLE
@property
def hvac_mode(self) -> HVACMode:
"""Return current operation ie. heat, cool, idle."""
mode = zone_mode(self._zone)
return self.map_mode_eph_hass(mode)
def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the operation mode."""
mode = self.map_mode_hass_eph(hvac_mode)
if mode is not None:
self._ember.set_mode_by_name(self._zone_name, mode)
else:
_LOGGER.error("Invalid operation mode provided %s", hvac_mode)
@property
def is_aux_heat(self):
"""Return true if aux heater."""
return zone_is_boost_active(self._zone)
def turn_aux_heat_on(self) -> None:
"""Turn auxiliary heater on."""
self._ember.activate_zone_boost(
self._zone_name, zone_target_temperature(self._zone)
)
def turn_aux_heat_off(self) -> None:
"""Turn auxiliary heater off."""
self._ember.deactivate_zone_boost(self._zone_name)
def set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
return
if self._hot_water:
return
if temperature == self.target_temperature:
return
if temperature > self.max_temp or temperature < self.min_temp:
return
self._ember.set_zone_target_temperature(self._zone_name, temperature)
@property
def min_temp(self):
"""Return the minimum temperature."""
# Hot water temp doesn't support being changed
if self._hot_water:
return zone_target_temperature(self._zone)
return 5.0
@property
def max_temp(self):
"""Return the maximum temperature."""
if self._hot_water:
return zone_target_temperature(self._zone)
if self._immersion:
return 90.0
return 35.0
def update(self) -> None:
"""Get the latest data."""
self._zone = self._ember.get_zone(self._zone_name)
@staticmethod
def map_mode_hass_eph(operation_mode):
"""Map from Home Assistant mode to eph mode."""
return getattr(ZoneMode, HA_STATE_TO_EPH.get(operation_mode), None)
@staticmethod
def map_mode_eph_hass(operation_mode):
"""Map from eph mode to Home Assistant mode."""
return EPH_TO_HA_STATE.get(operation_mode.name, HVACMode.HEAT_COOL)
Sorry for not getting back. I’ve started to add some of you as collaborators on the project to allow you to make updates
Sorry for not getting back. I’ve started to add some of you as collaborators on the project to allow you to make updates
Thanks @ttroy50. Can you please also give someone maintainer access to the pypi project so that it can be updated?
I bumped the Pypi to the 4.0 version now after I got maintainer access last night
So would just need someone to fix up climate.py on the offical home assistant component and we are good to go. I am currently using my own Custom Component. Don't feel confident enough to suggest all the changes. https://github.com/home-assistant/core/blob/dev/homeassistant/components/ephember/climate.py
Hi guys, great work on the API - I was almost certain things were working recently however I just started a fresh copy and I'm getting an error thrown at the following:
e.get_zones(); Traceback (most recent call last): File "
", line 1, in File "/usr/local/lib/python3.6/site-packages/pyephember/pyephember.py", line 676, in get_zones home_data = self.get_home() File "/usr/local/lib/python3.6/site-packages/pyephember/pyephember.py", line 650, in get_home data={"gateWayId": gateway_id} File "/usr/local/lib/python3.6/site-packages/pyephember/pyephember.py", line 413, in _http "{} response code".format(response.status_code) RuntimeError: 500 response code
Any idea what might be going on?
def get_home(self, gateway_id=None): """ Get the data about a home (API call: homesVT/zoneProgram). If no gateway_id is passed, the first gateway found is used. """ if gateway_id is None: if not self._homes: self._homes = self.list_homes() gateway_id = self._get_first_gateway_id()
response = self._http(
"homesVT/zoneProgram", send_token=True,
data={"gateWayId": gateway_id}
)
...............
In case you didn't get/see this from EPH, presumably relevant...