diff --git a/README.md b/README.md index 4c415bc..a09afba 100755 --- a/README.md +++ b/README.md @@ -155,6 +155,24 @@ Diagnostic entities: The entity information is updated every 30 seconds (default or other value is choosen otherwise during setup). +## Actions + +The following actions are available: + +- Davis Vantage: Set Davis Time + - Set the time of the Davis weather station +- Davis Vantage: Get Davis Time + - Get the time of the Davis weather station +- Davis Vantage: Get Raw Data + - Get the raw, unprocessed data from the last fetch +- Davis Vantage: Get Information + - Get information about firmware and diagnostics +- Davis Vantage: Set Yearly Rain + - Change yearly rain in clicks (depending on setup one click = 0.01" or 0.2mm) +- Davis Vantage: Set Archive Period + - Change archive period in minutes (accepted values: 1, 5, 10, 15, 30, 60, 120). WARNING: This will erase all archived data.= + + ## Known problems During first setup the communication to the weather station can be a bit tricky, resulting in an error saying the device didn't communicate. Please try again to set it up (can take up to 5 times). @@ -171,6 +189,6 @@ Due to the somewhat unstable hardware interface some communication runs result i [release-url]: https://github.com/MarcoGos/davis_vantage/releases [^1]: If Wind Gust doesn't show a value or "Unknown" make sure the Davis time is set correctly. You can check this by using action "Davis Vantage: Get Davis Time" and, if necessary, correct it by using action "Davis Vantage: Set Davis Time". -[^2]: The Archive Interval value can only be changed by using the original WeatherLink software. +[^2]: The Archive Interval value can be set by action "Set Archive Period" diff --git a/custom_components/davis_vantage/__init__.py b/custom_components/davis_vantage/__init__.py index ba5d194..b184d8c 100755 --- a/custom_components/davis_vantage/__init__.py +++ b/custom_components/davis_vantage/__init__.py @@ -4,6 +4,7 @@ from typing import Any import logging import json +import voluptuous as vol from zoneinfo import ZoneInfo from homeassistant.config_entries import ConfigEntry @@ -20,6 +21,8 @@ SERVICE_GET_DAVIS_TIME, SERVICE_GET_RAW_DATA, SERVICE_GET_INFO, + SERVICE_SET_YEARLY_RAIN, + SERVICE_SET_ARCHIVE_PERIOD, CONFIG_RAIN_COLLECTOR, CONFIG_STATION_MODEL, CONFIG_INTERVAL, @@ -97,7 +100,7 @@ async def get_davis_time(_: ServiceCall) -> dict[str, Any]: async def get_raw_data(_: ServiceCall) -> dict[str, Any]: raw_data = client.get_raw_data() - json_data = safe_serialize(raw_data) + json_data = Serializer.serialize(raw_data) return json.loads(json_data) async def get_info(_: ServiceCall) -> dict[str, Any]: @@ -109,6 +112,12 @@ async def get_info(_: ServiceCall) -> dict[str, Any]: "error": "Couldn't get firmware information from Davis weather station" } + async def set_yearly_rain(call: ServiceCall) -> None: + await client.async_set_yearly_rain(call.data["rain_clicks"]) + + async def set_archive_period(call: ServiceCall) -> None: + await client.async_set_archive_period(call.data["archive_period"]) + hass.services.async_register(DOMAIN, SERVICE_SET_DAVIS_TIME, set_davis_time) hass.services.async_register( DOMAIN, @@ -128,13 +137,25 @@ async def get_info(_: ServiceCall) -> dict[str, Any]: DOMAIN, SERVICE_GET_INFO, get_info, supports_response=SupportsResponse.ONLY ) - def safe_serialize(obj: Any): - # default = lambda obj: f"<>" # type: ignore - default = get_default(obj) - return json.dumps(obj, default=default) # type: ignore + hass.services.async_register( + DOMAIN, + SERVICE_SET_YEARLY_RAIN, + set_yearly_rain, + schema=vol.Schema({ + vol.Required('rain_clicks'): int + }) + ) - def get_default(obj: Any): - return f"<>" + hass.services.async_register( + DOMAIN, + SERVICE_SET_ARCHIVE_PERIOD, + set_archive_period, + schema=vol.Schema({ + vol.Required('archive_period'): vol.In( + ["1", "5", "10", "15", "30", "60", "120"] + ) + }) + ) return True @@ -150,3 +171,16 @@ async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: """Reload config entry.""" await async_unload_entry(hass, entry) await async_setup_entry(hass, entry) + +class Serializer(object): + @staticmethod + def serialize(obj): + def check(o): + for k, v in o.__dict__.items(): + try: + _ = json.dumps(v) + o.__dict__[k] = v + except TypeError: + o.__dict__[k] = str(v) + return o + return json.dumps(check(obj).__dict__, indent=2) \ No newline at end of file diff --git a/custom_components/davis_vantage/client.py b/custom_components/davis_vantage/client.py index 69e1355..fd0ce98 100755 --- a/custom_components/davis_vantage/client.py +++ b/custom_components/davis_vantage/client.py @@ -91,12 +91,15 @@ def get_current_data( pass if self._hass.data.get(DATA_ARCHIVE_PERIOD, None) is None: - self._hass.data.setdefault(DATA_ARCHIVE_PERIOD, self._vantagepro2.archive_period) + self._hass.data.setdefault( + DATA_ARCHIVE_PERIOD, self._vantagepro2.archive_period + ) try: end_datetime = datetime.now() - start_datetime = end_datetime - \ - timedelta(minutes=self._vantagepro2.archive_period * 2) # type: ignore + start_datetime = end_datetime - timedelta( + minutes=self._vantagepro2.archive_period * 2 + ) # type: ignore archives = self._vantagepro2.get_archives(start_datetime, end_datetime) # type: ignore except Exception: pass @@ -255,6 +258,41 @@ async def async_get_static_info(self) -> dict[str, Any] | None: _LOGGER.error("Couldn't get static info: %s", e) return info + def set_yearly_rain(self, rain_clicks: int) -> None: + """Set yearly rain of weather station.""" + try: + self._vantagepro2.link.open() + self._vantagepro2.set_yearly_rain(rain_clicks) + except Exception as e: + raise e + finally: + self._vantagepro2.link.close() + + async def async_set_yearly_rain(self, rain_clicks: int) -> None: + """Set yearly rain of weather station async.""" + try: + loop = asyncio.get_event_loop() + await loop.run_in_executor(None, self.set_yearly_rain, rain_clicks) + except Exception as e: + _LOGGER.error("Couldn't set yearly rain: %s", e) + + def set_archive_period(self, archive_period: int) -> None: + """Set archive periode, this will erase all archive data""" + try: + self._vantagepro2.link.open() + self._vantagepro2.set_archive_period(archive_period) + except Exception as e: + raise e + finally: + self._vantagepro2.link.close() + + async def async_set_archive_period(self, archive_period: int) -> None: + try: + loop = asyncio.get_event_loop() + await loop.run_in_executor(None, self.set_archive_period, archive_period) + except Exception as e: + _LOGGER.error("Couldn't set archive period: %s", e) + def add_additional_info(self, data: dict[str, Any]) -> None: if data["TempOut"] is not None: if data["HumOut"] is not None: diff --git a/custom_components/davis_vantage/const.py b/custom_components/davis_vantage/const.py index a127d42..b314519 100755 --- a/custom_components/davis_vantage/const.py +++ b/custom_components/davis_vantage/const.py @@ -3,7 +3,7 @@ NAME = "Davis Vantage" DOMAIN = "davis_vantage" MANUFACTURER = "Davis" -VERSION = "1.3.1" +VERSION = "1.3.2" DEFAULT_SYNC_INTERVAL = 30 # seconds DEFAULT_NAME = NAME @@ -20,6 +20,8 @@ SERVICE_GET_DAVIS_TIME = 'get_davis_time' SERVICE_GET_RAW_DATA = 'get_raw_data' SERVICE_GET_INFO = 'get_info' +SERVICE_SET_YEARLY_RAIN = 'set_yearly_rain' +SERVICE_SET_ARCHIVE_PERIOD = 'set_archive_period' MODEL_VANTAGE_PRO2 = 'Vantage Pro2' MODEL_VANTAGE_PRO2PLUS = 'Vantage Pro2 Plus' diff --git a/custom_components/davis_vantage/icons.json b/custom_components/davis_vantage/icons.json index bce38e1..bf6bc85 100755 --- a/custom_components/davis_vantage/icons.json +++ b/custom_components/davis_vantage/icons.json @@ -3,7 +3,9 @@ "set_davis_time": "mdi:clock-edit-outline", "get_davis_time": "mdi:clock-outline", "get_raw_data": "mdi:export", - "get_info": "mdi:information-box-outline" + "get_info": "mdi:information-box-outline", + "set_yearly_rain": "mdi:weather-pouring", + "set_archive_period": "mdi:archive-clock-outline" }, "entity": { "sensor": { diff --git a/custom_components/davis_vantage/manifest.json b/custom_components/davis_vantage/manifest.json index b836ac6..db06113 100755 --- a/custom_components/davis_vantage/manifest.json +++ b/custom_components/davis_vantage/manifest.json @@ -10,8 +10,8 @@ "homekit": {}, "iot_class": "local_polling", "issue_tracker": "https://github.com/MarcoGos/davis_vantage/issues", - "requirements": ["PyVantagePro-MarcoGos==0.3.13"], + "requirements": ["PyVantagePro-MarcoGos==0.3.16"], "ssdp": [], - "version": "1.3.1", + "version": "1.3.2", "zeroconf": [] } diff --git a/custom_components/davis_vantage/sensor.py b/custom_components/davis_vantage/sensor.py index 1bf0785..2e0c00d 100755 --- a/custom_components/davis_vantage/sensor.py +++ b/custom_components/davis_vantage/sensor.py @@ -49,7 +49,6 @@ def get_sensor_descriptions(model: str) -> list[SensorEntityDescription]: icon="mdi:clock-outline", device_class=SensorDeviceClass.TIMESTAMP, entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False ), SensorEntityDescription( key="LastSuccessTime", @@ -57,7 +56,6 @@ def get_sensor_descriptions(model: str) -> list[SensorEntityDescription]: icon="mdi:clock-outline", device_class=SensorDeviceClass.TIMESTAMP, entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False ), SensorEntityDescription( key="LastErrorTime", @@ -71,15 +69,13 @@ def get_sensor_descriptions(model: str) -> list[SensorEntityDescription]: key="LastError", translation_key="last_error_message", icon="mdi:message-alert-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False + entity_category=EntityCategory.DIAGNOSTIC ), SensorEntityDescription( key="ArchiveInterval", translation_key="archive_interval", icon="mdi:archive-clock-outline", entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, native_unit_of_measurement=UnitOfTime.MINUTES ), SensorEntityDescription( @@ -321,7 +317,6 @@ def get_sensor_descriptions(model: str) -> list[SensorEntityDescription]: translation_key="wind_speed_bft", icon="mdi:weather-windy", state_class="measurement", - entity_registry_enabled_default=False ), SensorEntityDescription( key="RainDay", @@ -439,7 +434,6 @@ def get_sensor_descriptions(model: str) -> list[SensorEntityDescription]: translation_key="rain_collector", icon="mdi:bucket-outline", entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, device_class=SensorDeviceClass.ENUM, options=[ RAIN_COLLECTOR_IMPERIAL, diff --git a/custom_components/davis_vantage/services.yaml b/custom_components/davis_vantage/services.yaml index a31b1c0..ee87437 100755 --- a/custom_components/davis_vantage/services.yaml +++ b/custom_components/davis_vantage/services.yaml @@ -9,4 +9,35 @@ get_raw_data: description: Get the raw, unprocessed data from the last fetch get_info: name: Get Information - description: Get information about firmware and diagnostics \ No newline at end of file + description: Get information about firmware and diagnostics +set_yearly_rain: + name: Set Yearly Rain + description: Set yearly rain in clicks + fields: + rain_clicks: + name: "Rain Clicks" + description: Rain in clicks (depending on setup one click = 0.01" or 0.2mm) + required: true + example: 500 + selector: + number: +set_archive_period: + name: Set Archive Period + description: "Set the archive period in minutes. Please restart HA after change. WARNING: All stored archive data will be erased!!!" + fields: + archive_period: + name: "Archive Period" + description: "Archive Period in minutes" + required: true + example: 10 + selector: + select: + options: + - "1" + - "5" + - "10" + - "15" + - "30" + - "60" + - "120" +