2018-12-05 21:22:06 +01:00
|
|
|
import binascii
|
|
|
|
import codecs
|
2019-02-26 18:31:40 +01:00
|
|
|
from datetime import datetime
|
2018-12-05 21:22:06 +01:00
|
|
|
import json
|
|
|
|
import logging
|
|
|
|
import os
|
2022-10-05 09:09:27 +02:00
|
|
|
from typing import Optional
|
2018-12-05 21:22:06 +01:00
|
|
|
|
2019-02-13 16:54:02 +01:00
|
|
|
from esphome import const
|
|
|
|
from esphome.core import CORE
|
2019-11-02 19:35:37 +01:00
|
|
|
from esphome.helpers import write_file_if_changed
|
2018-12-05 21:22:06 +01:00
|
|
|
|
2021-06-17 21:54:14 +02:00
|
|
|
from esphome.types import CoreType
|
2018-12-05 21:22:06 +01:00
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2022-10-05 09:09:27 +02:00
|
|
|
def storage_path() -> str:
|
2021-10-04 16:59:15 +02:00
|
|
|
return CORE.relative_internal_path(f"{CORE.config_filename}.json")
|
2018-12-05 21:22:06 +01:00
|
|
|
|
|
|
|
|
2022-10-05 09:09:27 +02:00
|
|
|
def ext_storage_path(base_path: str, config_filename: str) -> str:
|
2021-03-07 20:03:16 +01:00
|
|
|
return os.path.join(base_path, ".esphome", f"{config_filename}.json")
|
2018-12-05 21:22:06 +01:00
|
|
|
|
|
|
|
|
2022-10-05 09:09:27 +02:00
|
|
|
def esphome_storage_path(base_path: str) -> str:
|
2021-03-07 20:03:16 +01:00
|
|
|
return os.path.join(base_path, ".esphome", "esphome.json")
|
2018-12-05 21:22:06 +01:00
|
|
|
|
|
|
|
|
2022-10-05 09:09:27 +02:00
|
|
|
def trash_storage_path(base_path: str) -> str:
|
2021-03-07 20:03:16 +01:00
|
|
|
return os.path.join(base_path, ".esphome", "trash")
|
2019-03-16 22:24:26 +01:00
|
|
|
|
|
|
|
|
2018-12-05 21:22:06 +01:00
|
|
|
# pylint: disable=too-many-instance-attributes
|
2019-12-07 18:28:55 +01:00
|
|
|
class StorageJSON:
|
2021-03-07 20:03:16 +01:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
storage_version,
|
|
|
|
name,
|
|
|
|
comment,
|
|
|
|
esphome_version,
|
|
|
|
src_version,
|
|
|
|
address,
|
2021-10-28 00:46:55 +02:00
|
|
|
web_port,
|
2021-09-20 11:47:51 +02:00
|
|
|
target_platform,
|
2021-03-07 20:03:16 +01:00
|
|
|
build_path,
|
|
|
|
firmware_bin_path,
|
|
|
|
loaded_integrations,
|
|
|
|
):
|
2018-12-05 21:22:06 +01:00
|
|
|
# Version of the storage JSON schema
|
|
|
|
assert storage_version is None or isinstance(storage_version, int)
|
2022-10-05 09:09:27 +02:00
|
|
|
self.storage_version: int = storage_version
|
2018-12-05 21:22:06 +01:00
|
|
|
# The name of the node
|
2022-10-05 09:09:27 +02:00
|
|
|
self.name: str = name
|
2019-10-14 11:27:07 +02:00
|
|
|
# The comment of the node
|
2022-10-05 09:09:27 +02:00
|
|
|
self.comment: str = comment
|
2019-02-13 16:54:02 +01:00
|
|
|
# The esphome version this was compiled with
|
2022-10-05 09:09:27 +02:00
|
|
|
self.esphome_version: str = esphome_version
|
2018-12-05 21:22:06 +01:00
|
|
|
# The version of the file in src/main.cpp - Used to migrate the file
|
|
|
|
assert src_version is None or isinstance(src_version, int)
|
2022-10-05 09:09:27 +02:00
|
|
|
self.src_version: int = src_version
|
2018-12-05 21:22:06 +01:00
|
|
|
# Address of the ESP, for example livingroom.local or a static IP
|
2022-10-05 09:09:27 +02:00
|
|
|
self.address: str = address
|
2021-10-28 00:46:55 +02:00
|
|
|
# Web server port of the ESP, for example 80
|
|
|
|
assert web_port is None or isinstance(web_port, int)
|
2022-10-05 09:09:27 +02:00
|
|
|
self.web_port: int = web_port
|
2022-02-19 15:47:50 +01:00
|
|
|
# The type of hardware in use, like "ESP32", "ESP32C3", "ESP8266", etc.
|
2022-10-05 09:09:27 +02:00
|
|
|
self.target_platform: str = target_platform
|
2018-12-05 21:22:06 +01:00
|
|
|
# The absolute path to the platformio project
|
2022-10-05 09:09:27 +02:00
|
|
|
self.build_path: str = build_path
|
2018-12-05 21:22:06 +01:00
|
|
|
# The absolute path to the firmware binary
|
2022-10-05 09:09:27 +02:00
|
|
|
self.firmware_bin_path: str = firmware_bin_path
|
2019-05-28 10:19:17 +02:00
|
|
|
# A list of strings of names of loaded integrations
|
2022-10-05 09:09:27 +02:00
|
|
|
self.loaded_integrations: list[str] = loaded_integrations
|
2019-05-28 10:19:17 +02:00
|
|
|
self.loaded_integrations.sort()
|
2018-12-05 21:22:06 +01:00
|
|
|
|
|
|
|
def as_dict(self):
|
|
|
|
return {
|
2021-03-07 20:03:16 +01:00
|
|
|
"storage_version": self.storage_version,
|
|
|
|
"name": self.name,
|
|
|
|
"comment": self.comment,
|
|
|
|
"esphome_version": self.esphome_version,
|
|
|
|
"src_version": self.src_version,
|
|
|
|
"address": self.address,
|
2021-10-28 00:46:55 +02:00
|
|
|
"web_port": self.web_port,
|
2021-09-20 11:47:51 +02:00
|
|
|
"esp_platform": self.target_platform,
|
2021-03-07 20:03:16 +01:00
|
|
|
"build_path": self.build_path,
|
|
|
|
"firmware_bin_path": self.firmware_bin_path,
|
|
|
|
"loaded_integrations": self.loaded_integrations,
|
2018-12-05 21:22:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
def to_json(self):
|
2021-09-19 19:22:28 +02:00
|
|
|
return f"{json.dumps(self.as_dict(), indent=2)}\n"
|
2018-12-05 21:22:06 +01:00
|
|
|
|
|
|
|
def save(self, path):
|
2019-10-24 21:53:42 +02:00
|
|
|
write_file_if_changed(path, self.to_json())
|
2018-12-05 21:22:06 +01:00
|
|
|
|
|
|
|
@staticmethod
|
2021-03-07 20:03:16 +01:00
|
|
|
def from_esphome_core(
|
2022-10-05 09:09:27 +02:00
|
|
|
esph: CoreType, old: Optional["StorageJSON"]
|
|
|
|
) -> "StorageJSON":
|
2022-02-22 01:54:23 +01:00
|
|
|
hardware = esph.target_platform.upper()
|
2022-02-19 15:47:50 +01:00
|
|
|
if esph.is_esp32:
|
|
|
|
from esphome.components import esp32
|
|
|
|
|
|
|
|
hardware = esp32.get_esp32_variant(esph)
|
2018-12-05 21:22:06 +01:00
|
|
|
return StorageJSON(
|
|
|
|
storage_version=1,
|
|
|
|
name=esph.name,
|
2019-10-14 11:27:07 +02:00
|
|
|
comment=esph.comment,
|
2019-02-13 16:54:02 +01:00
|
|
|
esphome_version=const.__version__,
|
2018-12-05 21:22:06 +01:00
|
|
|
src_version=1,
|
|
|
|
address=esph.address,
|
2021-10-28 00:46:55 +02:00
|
|
|
web_port=esph.web_port,
|
2022-02-19 15:47:50 +01:00
|
|
|
target_platform=hardware,
|
2018-12-05 21:22:06 +01:00
|
|
|
build_path=esph.build_path,
|
|
|
|
firmware_bin_path=esph.firmware_bin,
|
2019-05-28 10:19:17 +02:00
|
|
|
loaded_integrations=list(esph.loaded_integrations),
|
2018-12-05 21:22:06 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
2022-10-20 05:50:39 +02:00
|
|
|
def from_wizard(name: str, address: str, platform: str) -> "StorageJSON":
|
2018-12-05 21:22:06 +01:00
|
|
|
return StorageJSON(
|
|
|
|
storage_version=1,
|
|
|
|
name=name,
|
2019-10-14 11:27:07 +02:00
|
|
|
comment=None,
|
2022-10-20 05:50:39 +02:00
|
|
|
esphome_version=None,
|
2018-12-05 21:22:06 +01:00
|
|
|
src_version=1,
|
|
|
|
address=address,
|
2021-10-28 00:46:55 +02:00
|
|
|
web_port=None,
|
2022-10-20 05:50:39 +02:00
|
|
|
target_platform=platform,
|
2018-12-05 21:22:06 +01:00
|
|
|
build_path=None,
|
|
|
|
firmware_bin_path=None,
|
2019-05-28 10:19:17 +02:00
|
|
|
loaded_integrations=[],
|
2018-12-05 21:22:06 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
2022-10-05 09:09:27 +02:00
|
|
|
def _load_impl(path: str) -> Optional["StorageJSON"]:
|
2021-03-07 20:03:16 +01:00
|
|
|
with codecs.open(path, "r", encoding="utf-8") as f_handle:
|
2019-10-24 21:53:42 +02:00
|
|
|
storage = json.load(f_handle)
|
2021-03-07 20:03:16 +01:00
|
|
|
storage_version = storage["storage_version"]
|
|
|
|
name = storage.get("name")
|
|
|
|
comment = storage.get("comment")
|
|
|
|
esphome_version = storage.get(
|
|
|
|
"esphome_version", storage.get("esphomeyaml_version")
|
|
|
|
)
|
|
|
|
src_version = storage.get("src_version")
|
|
|
|
address = storage.get("address")
|
2021-10-28 00:46:55 +02:00
|
|
|
web_port = storage.get("web_port")
|
2021-03-07 20:03:16 +01:00
|
|
|
esp_platform = storage.get("esp_platform")
|
|
|
|
build_path = storage.get("build_path")
|
|
|
|
firmware_bin_path = storage.get("firmware_bin_path")
|
|
|
|
loaded_integrations = storage.get("loaded_integrations", [])
|
|
|
|
return StorageJSON(
|
|
|
|
storage_version,
|
|
|
|
name,
|
|
|
|
comment,
|
|
|
|
esphome_version,
|
|
|
|
src_version,
|
|
|
|
address,
|
2021-10-28 00:46:55 +02:00
|
|
|
web_port,
|
2021-03-07 20:03:16 +01:00
|
|
|
esp_platform,
|
|
|
|
build_path,
|
|
|
|
firmware_bin_path,
|
|
|
|
loaded_integrations,
|
|
|
|
)
|
2018-12-05 21:22:06 +01:00
|
|
|
|
|
|
|
@staticmethod
|
2022-10-05 09:09:27 +02:00
|
|
|
def load(path: str) -> Optional["StorageJSON"]:
|
2018-12-05 21:22:06 +01:00
|
|
|
try:
|
|
|
|
return StorageJSON._load_impl(path)
|
|
|
|
except Exception: # pylint: disable=broad-except
|
|
|
|
return None
|
|
|
|
|
2022-10-05 09:09:27 +02:00
|
|
|
def __eq__(self, o) -> bool:
|
2018-12-05 21:22:06 +01:00
|
|
|
return isinstance(o, StorageJSON) and self.as_dict() == o.as_dict()
|
|
|
|
|
|
|
|
|
2019-12-07 18:28:55 +01:00
|
|
|
class EsphomeStorageJSON:
|
2021-03-07 20:03:16 +01:00
|
|
|
def __init__(
|
|
|
|
self, storage_version, cookie_secret, last_update_check, remote_version
|
|
|
|
):
|
2018-12-05 21:22:06 +01:00
|
|
|
# Version of the storage JSON schema
|
|
|
|
assert storage_version is None or isinstance(storage_version, int)
|
2022-10-05 09:09:27 +02:00
|
|
|
self.storage_version: int = storage_version
|
2018-12-05 21:22:06 +01:00
|
|
|
# The cookie secret for the dashboard
|
2022-10-05 09:09:27 +02:00
|
|
|
self.cookie_secret: str = cookie_secret
|
2019-02-13 16:54:02 +01:00
|
|
|
# The last time ESPHome checked for an update as an isoformat encoded str
|
2022-10-05 09:09:27 +02:00
|
|
|
self.last_update_check_str: str = last_update_check
|
2018-12-05 21:22:06 +01:00
|
|
|
# Cache of the version gotten in the last version check
|
2022-10-05 09:09:27 +02:00
|
|
|
self.remote_version: Optional[str] = remote_version
|
2018-12-05 21:22:06 +01:00
|
|
|
|
2022-10-05 09:09:27 +02:00
|
|
|
def as_dict(self) -> dict:
|
2018-12-05 21:22:06 +01:00
|
|
|
return {
|
2021-03-07 20:03:16 +01:00
|
|
|
"storage_version": self.storage_version,
|
|
|
|
"cookie_secret": self.cookie_secret,
|
|
|
|
"last_update_check": self.last_update_check_str,
|
|
|
|
"remote_version": self.remote_version,
|
2018-12-05 21:22:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@property
|
2022-10-05 09:09:27 +02:00
|
|
|
def last_update_check(self) -> Optional[datetime]:
|
2018-12-05 21:22:06 +01:00
|
|
|
try:
|
|
|
|
return datetime.strptime(self.last_update_check_str, "%Y-%m-%dT%H:%M:%S")
|
|
|
|
except Exception: # pylint: disable=broad-except
|
|
|
|
return None
|
|
|
|
|
|
|
|
@last_update_check.setter
|
2022-10-05 09:09:27 +02:00
|
|
|
def last_update_check(self, new: datetime) -> None:
|
2018-12-05 21:22:06 +01:00
|
|
|
self.last_update_check_str = new.strftime("%Y-%m-%dT%H:%M:%S")
|
|
|
|
|
2022-10-05 09:09:27 +02:00
|
|
|
def to_json(self) -> dict:
|
2021-09-19 19:22:28 +02:00
|
|
|
return f"{json.dumps(self.as_dict(), indent=2)}\n"
|
2018-12-05 21:22:06 +01:00
|
|
|
|
2022-10-05 09:09:27 +02:00
|
|
|
def save(self, path: str) -> None:
|
2019-10-24 21:53:42 +02:00
|
|
|
write_file_if_changed(path, self.to_json())
|
2018-12-05 21:22:06 +01:00
|
|
|
|
|
|
|
@staticmethod
|
2022-10-05 09:09:27 +02:00
|
|
|
def _load_impl(path: str) -> Optional["EsphomeStorageJSON"]:
|
2021-03-07 20:03:16 +01:00
|
|
|
with codecs.open(path, "r", encoding="utf-8") as f_handle:
|
2019-10-24 21:53:42 +02:00
|
|
|
storage = json.load(f_handle)
|
2021-03-07 20:03:16 +01:00
|
|
|
storage_version = storage["storage_version"]
|
|
|
|
cookie_secret = storage.get("cookie_secret")
|
|
|
|
last_update_check = storage.get("last_update_check")
|
|
|
|
remote_version = storage.get("remote_version")
|
|
|
|
return EsphomeStorageJSON(
|
|
|
|
storage_version, cookie_secret, last_update_check, remote_version
|
|
|
|
)
|
2018-12-05 21:22:06 +01:00
|
|
|
|
|
|
|
@staticmethod
|
2022-10-05 09:09:27 +02:00
|
|
|
def load(path: str) -> Optional["EsphomeStorageJSON"]:
|
2018-12-05 21:22:06 +01:00
|
|
|
try:
|
2019-02-13 16:54:02 +01:00
|
|
|
return EsphomeStorageJSON._load_impl(path)
|
2018-12-05 21:22:06 +01:00
|
|
|
except Exception: # pylint: disable=broad-except
|
|
|
|
return None
|
|
|
|
|
|
|
|
@staticmethod
|
2022-10-05 09:09:27 +02:00
|
|
|
def get_default() -> "EsphomeStorageJSON":
|
2019-02-13 16:54:02 +01:00
|
|
|
return EsphomeStorageJSON(
|
2018-12-05 21:22:06 +01:00
|
|
|
storage_version=1,
|
2019-12-07 18:28:55 +01:00
|
|
|
cookie_secret=binascii.hexlify(os.urandom(64)).decode(),
|
2018-12-05 21:22:06 +01:00
|
|
|
last_update_check=None,
|
|
|
|
remote_version=None,
|
|
|
|
)
|
|
|
|
|
2022-10-05 09:09:27 +02:00
|
|
|
def __eq__(self, o) -> bool:
|
2019-02-13 16:54:02 +01:00
|
|
|
return isinstance(o, EsphomeStorageJSON) and self.as_dict() == o.as_dict()
|