Skip to content

Instantly share code, notes, and snippets.

@fredrike
Last active May 14, 2020 07:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fredrike/c9e99a3ae59a39d8df9c811456b8012f to your computer and use it in GitHub Desktop.
Save fredrike/c9e99a3ae59a39d8df9c811456b8012f to your computer and use it in GitHub Desktop.
ChargeAmps4HomeAssistant
"""Charge Amps sensor"""

Custom component

Example configuration:

sensor:
  - platform: chargeamps
    username: <username>
    password: <password>
  - platform: template
    sensors:
      chargeamps_watt:
        value_template: "{{ state_attr('sensor.mjovik', 'Energy') }}"
        unit_of_measurement: "W"
"""Consts."""
from datetime import timedelta
API_URL = "https://chargeampsapi.azurewebsites.net/chargeamps/"
AUTH_URL = API_URL + "auth/login"
OWNED_CHARGEPOINTS_URL = API_URL + "users/{uid}/chargepoints/owned"
STATS_URL = API_URL + "stats/users/{uid}"
CHARGEPOINTS_URL = API_URL + "chargepoints"
TIMEOUT = timedelta(seconds=10)
HEADERS = {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json;charset=UTF-8",
}
AUTH_STRING = '{{"email":"{}","password":"{}","hostName":"my.charge.space"}}'
sensor:
- platform: chargeamps
username: !secret chargeamps_user
password: !secret chargeamps_pass
- platform: template
sensors:
chargeamps_watt:
friendly_name: "Charge Amps"
value_template: "{{ state_attr('sensor.chargeamps', 'Energy') | float }}"
unit_of_measurement: "W"
device_class: power
chargeamps_phase:
friendly_name: "Charge Amps Phase"
value_template: "{{ state_attr('sensor.chargeamps', 'installationPhase') }}"
binary_sensor:
- platform: template
sensors:
charge_amps_plugged:
friendly_name: "Charge Amps plug"
device_class: plug
value_template: "{{ is_state_attr('sensor.chargeamps', 'Connector 0 state',
'B2Connected') or is_state_attr('sensor.chargeamps', 'Connector 0 state', 'C2ConnectedCharging') }}"
charge_amps_charging:
friendly_name: "Charge Amps charge"
device_class: battery_charging
icon_template: mdi:car-electric
value_template: "{{ is_state_attr('sensor.chargeamps', 'Connector 0 state', 'C2ConnectedCharging') }}"
switch:
- platform: template
switches:
chargeamps_socket:
friendly_name: "Charge Amp's Socket"
icon_template: mdi:power-plug
value_template: "{{ is_state_attr('sensor.chargeamps', 'Connector 1', 'On') }}"
turn_off:
service: chargeamps.change_settings
data:
entity_id: none.none
setting: '{"connectors": [{},{"mode": "Off"}]}'
turn_on:
service: chargeamps.change_settings
data:
entity_id: none.none
setting: '{"connectors": [{},{"mode": "On"}]}'
light:
- platform: template
lights:
chargeamps_downlight:
friendly_name: "Charge Amp's Light"
value_template: >-
{% if state_attr('sensor.chargeamps', 'Light') %}
on
{% else %}
off
{% endif %}
turn_off:
service: chargeamps.change_settings
data:
entity_id: none.none
setting: '{"downlight": false}'
turn_on:
service: chargeamps.change_settings
data:
entity_id: none.none
setting: '{"downlight": true}'
chargeamps_dimmer:
friendly_name: "Charge Amp's LED Brightness"
value_template: >-
{{ state_attr('sensor.chargeamps', 'LED Brightness') > 0 }}
level_template: >-
{%if state_attr('sensor.chargeamps', 'LED Brightness') == 0%}0
{%elif state_attr('sensor.chargeamps', 'LED Brightness') == 8%}85
{%elif state_attr('sensor.chargeamps', 'LED Brightness') == 64%}170
{%else%} 255
{%endif%}
turn_off:
service: chargeamps.change_settings
data:
entity_id: none.none
setting: '{"dimmer": 0}'
turn_on:
service: chargeamps.change_settings
data:
entity_id: none.none
setting: '{"dimmer": 8}'
set_level:
service: chargeamps.change_settings
data_template:
entity_id: none.none
setting: >-
"'{\"dimmer\": {%if brightness < 128%}8{%elif brightness<192%}64{%else%}255{%endif%}}'"
chargeamps_current:
friendly_name: "Charge Amp's Charger"
icon_template: mdi:car-electric
value_template: "{{ is_state_attr('sensor.chargeamps', 'Connector 0', 'On') }}"
level_template: "{{ ((state_attr('sensor.chargeamps', 'userCurrent') | int) - 10) * 41.5 |int }}"
turn_off:
service: chargeamps.change_settings
data:
entity_id: none.none
setting: '{"connectors": [{"mode": "Off"}]}'
turn_on:
service: chargeamps.change_settings
data:
entity_id: none.none
setting: '{"connectors": [{"mode": "On"}]}'
set_level:
service: chargeamps.change_settings
data_template:
entity_id: none.none
setting: "{{ '{\"connectors\": [{\"userCurrent\": ' + (brightness / 41.5 | round(0) + 10) | int | string + '}]}' }}"
views:
- icon: 'mdi:car-electric'
theme: light-navy_blue
badges:
- entity: sensor.chargeamps
- entity: sensor.chargeamps_watt
- entity: binary_sensor.charge_amps_charging
- entity: binary_sensor.charge_amps_plugged
cards:
- cards:
- content: >
{% if states("sensor.chargeamps_watt") |float > 0.0 %} The car is
currently charging via **{{ state_attr("sensor.chargeamps",
"installationPhase") }}** @ {{state_attr("sensor.chargeamps",
"userCurrent") | int }} Amp ({{
states("sensor.chargeamps_watt")|round(-2)|int }}W){% else %} The
car is not charging. ChargeAmps is configured to charge from **{{
state_attr("sensor.chargeamps", "installationPhase") }}** @
{{state_attr("sensor.mjovik", "userCurrent") | int }} Amp. {%
endif %}
type: markdown
- entities:
- entity: sensor.chargeamps
icon: 'mdi:flash'
- entity: sensor.chargeamps_watt
- entity: binary_sensor.charge_amps_plugged
- entity: binary_sensor.charge_amps_charging
type: glance
- entity: light.chargeamps_current
icon: 'mdi:car-electric'
type: light
- cards:
- entity: sensor.chargeamps
icon: 'mdi:power-plug'
name: L1
tap_action:
action: call-service
service: chargeamps.change_settings
service-data: '{"connectors": [{"installationPhase": "L1"}]}'
type: entity-button
- entity: sensor.chargeamps
icon: 'mdi:power-plug'
name: L2
tap_action:
action: call-service
service: chargeamps.change_settings
service-data: '{"connectors": [{"installationPhase": "L2"}]}'
type: entity-button
- entity: sensor.chargeamps
icon: 'mdi:power-plug'
name: L3
tap_action:
action: call-service
service: chargeamps.change_settings
service-data: '{"connectors": [{"installationPhase": "L3"}]}'
type: entity-button
type: horizontal-stack
- type: button
tap_action:
action: toggle
hold_action:
action: more-info
show_icon: true
show_name: true
entity: switch.chargeamps_socket
- type: horizontal-stack
cards:
- type: light
entity: light.chargeamps_downlight
- type: light
entity: light.chargeamps_dimmer
type: vertical-stack
title: Charge Amps
{
"domain": "chargeamps",
"name": "ChargeAmps Sensor",
"documentation": "https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_sensor/",
"dependencies": ["http"],
"requirements": [],
"codeowners": ["@fredrike"]
}
"""Platfor for sensor integration."""
import asyncio
import collections
import logging
import json
import aiohttp
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, ENERGY_KILO_WATT_HOUR
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from .const import *
_LOGGER = logging.getLogger(__name__)
# Validation of the user's configuration
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
}
)
DOMAIN = 'chargeamps'
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the sensor platform."""
# Assign configuration variables.
# The configuration check takes care they are present.
username = config[CONF_USERNAME]
password = config.get(CONF_PASSWORD)
# Setup connection with devices/cloud
authenticate_info = await ChargeAmps.authenticate(username, password)
# Verify that passed in configuration works
if not authenticate_info:
_LOGGER.error("Could not authenticate.")
return
# Add devices
chargeamps = [ChargeAmps(authenticate_info, username, password)]
hass.data[DOMAIN] = chargeamps
async_add_entities(chargeamps, True)
@callback
def service_settings(call):
"""Handle service call."""
settings = call.data.get("setting", "{}")
entity_id = call.data.get("entity_id", "")
_LOGGER.warning("Sending settings: {} to {}".format(settings, entity_id))
hass.loop.create_task(hass.data[DOMAIN][0].send(settings))
hass.services.async_register(DOMAIN, "change_settings", service_settings)
class ChargeAmps(Entity):
"""Representation of an ChargeAmps."""
def __init__(self, authenticate_info, username, password):
"""Initialize an AwesomeLight."""
self._authenticate_info = authenticate_info
self._username = username
self._password = password
self._state = {'owned': [{}], 'stats': {}}
@staticmethod
async def authenticate(username, password):
"""Authenticate user."""
async with aiohttp.ClientSession() as session:
async with session.post(
AUTH_URL, headers=HEADERS, data=AUTH_STRING.format(username, password),
) as resp:
if resp.status == 200:
return await resp.json()
return False
@property
def unique_id(self):
"""Return the unique id."""
return self._state["owned"][0].get("id", "ChargeAmps")
@property
def name(self):
"""Return the display name of this light."""
return self._state["owned"][0].get("name", "ChargeAmps")
@property
def state(self):
"""Return the state of the sensor."""
return self._state["stats"].get("totalConsumptionFromDayOne")
@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return ENERGY_KILO_WATT_HOUR
@property
def icon(self):
"""Return icon."""
return "mdi:flash"
@property
def device_state_attributes(self):
"""Return status of device."""
args = {}
args['id'] = self.entity_id
_LOGGER.debug(self._state["owned"])
for i, connector in enumerate(self._state["owned"][0]["connectors"]):
args[f'Connector {i}'] = connector["mode"]
args[f'Connector {i} state'] = connector["evConnectorState"]
args['Light'] = self._state["owned"][0]["downLight"]
args['LED Brightness'] = self._state["owned"][0]["dimmer"]
if "connectors" in self._state["owned"][0]:
connector = self._state["owned"][0]["connectors"][0]
args['userCurrent'] = connector['userCurrent']
args['totalConsumptionKwh'] = round(connector['totalConsumptionKwh'], 3)
args['installationPhase'] = connector['installationPhase']
effect = 0
for i in (1, 2, 3):
effect += connector["voltage{}".format(i)] * connector["current{}".format(i)]
args[f"voltage_L{i}".title()] = connector[f"voltage{i}"]
args[f"current_L{i}".title()] = connector[f"current{i}"]
args['Energy'] = round(effect, 1)
return args
async def _async_update(self):
"""Update states."""
async with aiohttp.ClientSession() as session:
headers = {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json;charset=UTF-8",
"Authorization": "Token {}".format(self._authenticate_info["token"]),
}
uid = self._authenticate_info["user"]["id"]
res = await asyncio.gather(
session.get(OWNED_CHARGEPOINTS_URL.format(uid=uid), headers=headers,),
session.get(STATS_URL.format(uid=uid), headers=headers,),
)
if (res[0].status == 200) and (res[1].status == 200):
self._state["owned"] = await res[0].json()
self._state["stats"] = await res[1].json()
return 200
return res[0].status
async def async_update(self, **kwargs):
"""Update states."""
status = await self._async_update()
if status == 401:
self._authenticate_info = await self.authenticate(self._username, self._password)
# print("Token: %s" % auth["token"])
await self._async_update()
async def send(self, settings):
"""Send command to chargeamps."""
def deep_update(source, overrides):
"""
Update a nested dictionary or similar mapping.
Modify ``source`` in place.
"""
for key, value in overrides.items():
if isinstance(value, collections.Mapping) and value:
returned = deep_update(source.get(key, {}), value)
source[key] = returned
elif isinstance(value, list):
returned = []
for i, val in enumerate(source.get(key, [])):
if i < len(value):
returned.append(deep_update(val, value[i]))
source[key] = returned
else:
source[key] = overrides[key]
return source
settings = json.loads(settings)
owned = deep_update(self._state["owned"][0].copy(), settings)
_LOGGER.debug("Sending settings: %s", owned)
async with aiohttp.ClientSession() as session:
headers = {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json;charset=UTF-8",
"Authorization": "Token {}".format(self._authenticate_info["token"]),
}
res = await session.put(CHARGEPOINTS_URL, json=owned, headers=headers,)
if res.status == 200:
_LOGGER.debug("OK")
self.async_schedule_update_ha_state(force_refresh=True)
else:
await _LOGGER.warning(res.text())
change_settings:
description: Change a device's settings.
fields:
entiy_id:
description: Name of entity to change.
example: 'sensor.chargeamps'
setting:
description: Json string of settings.
example: '{"connectors": [{"mode": "Off"},{"mode": "Off"}], "downlight": false, "dimmer": 64}'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment