Skip to content

Commit

Permalink
New Platform Live sensors | No History or News Sensor
Browse files Browse the repository at this point in the history
New Platform Live sensors | No History or News Sensor
  • Loading branch information
jobvk authored Aug 16, 2022
2 parents af3c88e + 28d4585 commit d906e7e
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 271 deletions.
2 changes: 1 addition & 1 deletion custom_components/windcentrale/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from homeassistant import config_entries, core
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import DOMAIN, PLATFORMS
from . import wind
from .const import *

async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the Windcentrale component."""
Expand Down
23 changes: 14 additions & 9 deletions custom_components/windcentrale/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Platform for binary_sensor integration."""
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.components.binary_sensor import BinarySensorEntity
from .const import DOMAIN

Expand All @@ -13,7 +14,7 @@ async def async_setup_entry(hass, config_entry, async_add_devices):
if new_devices:
async_add_devices(new_devices)

class SensorBase(BinarySensorEntity):
class SensorBase(RestoreEntity, BinarySensorEntity):
"""Base representation of a windcentrale turbine."""

def __init__(self, windturbine):
Expand All @@ -24,7 +25,7 @@ def __init__(self, windturbine):
def device_info(self):
"""Information about this wind turbine"""
return {
"identifiers": {(DOMAIN, self._windturbine.windturbine_id)},
"identifiers": {(DOMAIN, self._windturbine.id)},
"name": self._windturbine.name,
"model": self._windturbine.model,
"manufacturer": self._windturbine.manufacturer,
Expand All @@ -33,15 +34,14 @@ def device_info(self):
@property
def available(self) -> bool:
"""Return true if windturbine live sensor is available."""
return self._windturbine.live_status
return True

class PulsingSensor(SensorBase):
"""Representation of a Sensor."""

def __init__(self, windturbine):
"""Initialize the sensor."""
super().__init__(windturbine)
self._state = None

@property
def unique_id(self) -> str:
Expand All @@ -63,10 +63,15 @@ def icon(self) -> str:
"""Icon for the sensor."""
return "mdi:pulse"

async def async_added_to_hass(self):
"""Call when entity is about to be added to Home Assistant."""
if (state := await self.async_get_last_state()) is None:
self._state = None
return

self._state = state.state

def update(self):
"""Update the sensor."""
if self._windturbine.live_data:
self._state = self._windturbine.live_data["pulsating"]
return self._state
else:
return None
if self._windturbine.live_data is not None:
self._state = self._windturbine.live_data["pulsating"]
50 changes: 25 additions & 25 deletions custom_components/windcentrale/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Config flow for windcentrale integration."""
import logging
import json
import voluptuous as vol
from homeassistant import config_entries, core, exceptions
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, CONF_SHOW_ON_MAP
Expand All @@ -25,20 +26,20 @@ def async_get_options_flow(config_entry):
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)


async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}
if user_input is not None:
await self.async_set_unique_id(DOMAIN)
self._abort_if_unique_id_configured()
try:
windcentrale_data = await validate_input(self.hass, user_input)
return self.async_create_entry(title="Windcentrale", data=windcentrale_data)
except BadCredentials:
errors["base"] = "bad_credentials"
except Exception:
_LOGGER.error("Unexpected exception when submitting windcentrale config")
user_input = await validate_input(self.hass, user_input)
return self.async_create_entry(title="Windcentrale", data=user_input)
except InvalidUserCredentails:
errors["base"] = "invalid_user_credentails"
except Exception as e:
_LOGGER.error(e)
# _LOGGER.error("Unexpected exception when submitting windcentrale config")
errors["base"] = "unknown"

# If there is no user input or there were errors, show the form again, including any errors that were found with the input.
Expand All @@ -56,31 +57,30 @@ async def async_step_init(self, user_input=None):
try:
return self.async_create_entry(title="", data=user_input)
except Exception:
_LOGGER.error("Unexpected exception when chaning windcentrale options")
_LOGGER.error("Unexpected exception when changing windcentrale options")
errors["base"] = "unknown"

return self.async_show_form(step_id="init", data_schema= vol.Schema({
return self.async_show_form(step_id="init", data_schema= vol.Schema({
vol.Optional(CONF_NEWS_FILTER, default=self.config_entry.options.get(CONF_NEWS_FILTER, NEWS_FILTER[0])): vol.In(NEWS_FILTER),
vol.Optional(CONF_SHOW_ON_MAP, default=self.config_entry.options.get(CONF_SHOW_ON_MAP, DEFAULT_SHOW_ON_MAP)): bool
}),
}),
errors=errors)

async def validate_input(hass, data: dict):
async def validate_input(hass, user_input: dict):
"""Validate the user input"""
windCredentials = Credentials(hass, data[CONF_EMAIL], data[CONF_PASSWORD])
result = await windCredentials.test_credentails()
if result == "Bad credentials":
raise BadCredentials
elif result == 'error':
raise Exception
elif result == 'succes':
windshare_dict = await windCredentials.collect_windshares()
credentails = Credentials(hass, user_input[CONF_EMAIL], user_input[CONF_PASSWORD])
result_user_credentails = await credentails.authenticate_user_credentails()
user_input[CONF_TOKEN_HEADER] = json.dumps(result_user_credentails)
if result_user_credentails == "invalid_user_credentails":
raise InvalidUserCredentails
else:
result_projects_windshares = await credentails.collect_projects_windshares()
for windturbine in WINDTURBINES_LIST:
if windturbine in windshare_dict:
data[windturbine] = windshare_dict[windturbine]["shares"]
if windturbine in result_projects_windshares:
user_input[windturbine] = result_projects_windshares[windturbine].toJSON()
else:
data[windturbine] = 0
return data
user_input[windturbine] = None
return user_input

class BadCredentials(exceptions.HomeAssistantError):
"""Error to indicate there is an invalid number."""
class InvalidUserCredentails(exceptions.HomeAssistantError):
"""Error to indicate there is an incorrect username or password."""
66 changes: 32 additions & 34 deletions custom_components/windcentrale/const.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Constants for the Windcentrale integration."""
import json
import datetime as dt
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.const import (
Expand All @@ -15,44 +16,47 @@
PLATFORMS = ["sensor","binary_sensor"]
NEWS_FILTER = ["All News", "General + Your Windturbine(s)", "Only Your Windturbine(s)"]

CONF_TOKEN_HEADER = "token_header"
CONF_NEWS_FILTER = "news_filter"

LIVE_INTERVAL = 10 #sec
PRODUCTION_INTERVAL = 1 #min
NEWS_INTERVAL = 5 #min
TOKEN_INTERVAL = 55 #min

DEFAULT_NEWS_FILTER = NEWS_FILTER[0]
DEFAULT_SHOW_ON_MAP = False

# Format:
# Name: [id, Manufacturer, Model, Latitude, Longitude, StartDate]
# Name: [Manufacturer, Model, Location, Latitude, Longitude, Total Shares, StartDate]
WINDTURBINES_LIST = {
"De Grote Geert": [1, "Enercon", "E-70", "Meedhuizen", 53.27988, 6.9594, dt.datetime(2013, 1, 1)],
"De Jonge Held": [2, "Enercon", "E-70", "Meedhuizen", 53.27725, 6.95859, dt.datetime(2013, 1, 1)],
"Het Rode Hert": [31, "Vestas", "V80", "Culemborg", 51.935829, 5.192112, dt.datetime(2014, 1, 1)] ,
"De Ranke Zwaan": [ 41, "Vestas", "V80", "Culemborg", 51.934916, 5.199874, dt.datetime(2014, 1, 1)],
"De Witte Juffer": [51, "Vestas", "V80", "Culemborg", 51.935174, 5.195846,dt.datetime(2014, 1, 1)],
"De Bonte Hen": [111, "Vestas", "V52", "Burgerbrug", 52.757049, 4.684686, dt.datetime(2014, 1, 1)],
"De Trouwe Wachter": [121, "Vestas", "V52", "Burgerbrug", 52.758741, 4.686049, dt.datetime(2014, 1, 1)],
"De Blauwe Reiger": [131, "Vestas", "V52", "Burgerbrug", 52.760478, 4.687449, dt.datetime(2014, 1, 1)],
"De Vier Winden": [141, "Vestas", "V52", "Burgerbrug", 52.762214, 4.688828, dt.datetime(2014, 7, 1)],
"De Boerenzwaluw": [191, "Enercon", "E-44", "Burum", 53.265376, 6.214152, dt.datetime(2016, 8, 1)],
"Het Vliegend Hert": [211, "Lagerwey", "L82", "Rouveen", 52.59131, 6.22014, dt.datetime(2018, 9, 15)]
"De Grote Geert": ["Enercon", "E-70", "Meedhuizen", 53.27988, 6.9594, 9910, dt.datetime(2013, 1, 1)],
"De Jonge Held": ["Enercon", "E-70", "Meedhuizen", 53.27725, 6.95859, 10154, dt.datetime(2013, 1, 1)],
"Het Rode Hert": ["Vestas", "V80", "Culemborg", 51.935829, 5.192112, 6648, dt.datetime(2014, 1, 1)] ,
"De Ranke Zwaan": ["Vestas", "V80", "Culemborg", 51.934916, 5.199874, 6164, dt.datetime(2014, 1, 1)],
"De Witte Juffer": ["Vestas", "V80", "Culemborg", 51.935174, 5.195846, 5721, dt.datetime(2014, 1, 1)],
"De Bonte Hen": ["Vestas", "V52", "Burgerbrug", 52.757049, 4.684686, 5579, dt.datetime(2014, 1, 1)],
"De Trouwe Wachter": ["Vestas", "V52", "Burgerbrug", 52.758741, 4.686049, 5602, dt.datetime(2014, 1, 1)],
"De Blauwe Reiger": ["Vestas", "V52", "Burgerbrug", 52.760478, 4.687449, 5534, dt.datetime(2014, 1, 1)],
"De Vier Winden": ["Vestas", "V52", "Burgerbrug", 52.762214, 4.688828, 5512, dt.datetime(2014, 7, 1)],
"De Boerenzwaluw": ["Enercon", "E-44", "Burum", 53.265376, 6.214152, 3000, dt.datetime(2016, 8, 1)],
"Het Vliegend Hert": ["Lagerwey", "L82", "Rouveen", 52.59131, 6.22014, 9751, dt.datetime(2018, 9, 15)]
}

# Format:
# Id: [Name, Device Class, Unit Of Measurement, Icon, Json Key]
LIVE_SENSOR_TYPES = {
"windturbine": [None, SensorDeviceClass.POWER, POWER_WATT, "mdi:wind-turbine", "powerAbsWd"],
"windspeed": ["Wind Speed", None, "BFT", "mdi:windsock", "windSpeed"],
"winddirection": ["Wind Direction", None, None, "mdi:compass", "windDirection"],
"powerabstot": ["Power Production Total", SensorDeviceClass.POWER, POWER_KILO_WATT, None, "powerAbsTot"],
"powerabswd": ["Power Production Per Share", SensorDeviceClass.POWER, POWER_WATT, None, "powerAbsWd"],
"powerrel": ["Max Power", None, PERCENTAGE, "mdi:chart-timeline-variant", "powerRel"],
"windturbine": [None, SensorDeviceClass.POWER, POWER_WATT, "mdi:wind-turbine", "power_per_share"],
"windspeed": ["Wind Speed", None, "BFT", "mdi:windsock", "wind_power"],
"winddirection": ["Wind Direction", None, None, "mdi:compass", "wind_direction"],
"powertotal": ["Power Total", SensorDeviceClass.POWER, POWER_KILO_WATT, None, "power"],
"powerpershare": ["Power Per Share", SensorDeviceClass.POWER, POWER_WATT, None, "power_per_share"],
"powerpercentage": ["Power Percentage", None, PERCENTAGE, "mdi:percent", "power_percentage"],
"rpm": ["Revolutions Per Minute", None, "RPM", "mdi:speedometer", "rpm"],
"kwh": ["kWh", SensorDeviceClass.ENERGY, ENERGY_KILO_WATT_HOUR, None, "kwh"],
"hoursrunthisyear": ["Hours Run This Year", None, TIME_HOURS, "mdi:calendar-clock", "hoursRunThisYear"],
"runpercentage": ["Run Percentage", None, PERCENTAGE, "mdi:percent", "runPercentage"],
"energy": ["Energy Management", SensorDeviceClass.ENERGY, ENERGY_KILO_WATT_HOUR, None, "year_production"],
"yearproduction": ["Production This Year", SensorDeviceClass.ENERGY, ENERGY_KILO_WATT_HOUR, None, "year_production"],
"runtimeyear": ["Hours Run This Year", None, TIME_HOURS, "mdi:calendar-clock", "year_runtime"],
"runtimetotal": ["Hours Run Total", None, TIME_HOURS, "mdi:calendar-clock", "total_runtime"],
"timestamp": ["Last Update", SensorDeviceClass.TIMESTAMP, None, None, "timestamp"]
}

Expand All @@ -67,16 +71,10 @@
}

class powerProducer:
def __init__(self, powerproducer_id, windturbine_id, name):
self.id = powerproducer_id
self.windturbine_id = windturbine_id
self.name = name
self.shares = 0

def add_shares(self):
self.shares = self.shares + 1

class Shares:
def __init__(self, share_id, powerProducer):
self.id = share_id
self.powerProducer = powerProducer
def __init__(self, windturbine_name, windturbine_code, windturbine_shares):
self.name = windturbine_name
self.code = windturbine_code
self.shares = windturbine_shares

def toJSON(self):
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4)
5 changes: 3 additions & 2 deletions custom_components/windcentrale/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
"config_flow": true,
"documentation": "https://github.com/jobvk/Home-Assistant-Windcentrale",
"issue_tracker": "https://github.com/jobvk/Home-Assistant-Windcentrale/issues",
"requirements": [],
"requirements": ["boto3==1.20.40", "pycognito==2022.5.0"],
"iot_class": "cloud_polling",
"dependencies": [],
"codeowners": ["@jobvk"],
"version": "0.1.4"
"version": "0.2.0",
"loggers": ["boto3"]
}
Loading

0 comments on commit d906e7e

Please sign in to comment.