1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
#!/usr/bin/env python3
"""Script to generate Multilevel Sensor CC constants."""
from __future__ import annotations
from collections import defaultdict
from collections.abc import Callable, Mapping
import pathlib
from const import AUTO_GEN_POST, AUTO_GEN_PRE
from helpers import (
enum_name_format,
format_for_class_name,
get_json,
get_manually_written_code,
get_registry_location,
run_black,
separate_camel_case,
)
CONST_FILE_PATH = (
pathlib.Path(__file__).parent.parent
/ "zwave_js_server/const/command_class/multilevel_sensor.py"
)
def normalize_scale_definition(scale_definitions: dict[str, dict]) -> dict[str, int]:
"""Convert a scales definition dictionary into a normalized dictionary."""
scale_def_ = {}
for scale_id, scale_props in scale_definitions.items():
scale_name_ = enum_name_format(scale_props["label"], True)
scale_def_[scale_name_] = scale_id
return dict(sorted(scale_def_.items(), key=lambda kv: kv[0]))
scales = {}
sensors = {}
for sensor_props in get_json("sensors.json"):
sensor_id = sensor_props["key"]
scale_def = sensor_props["scales"]
remove_parenthesis_ = True
if sensor_id in (87, 88):
remove_parenthesis_ = False
sensor_name = enum_name_format(sensor_props["label"], remove_parenthesis_)
sensors[sensor_name] = {"id": sensor_id, "label": sensor_props["label"]}
scale_name = separate_camel_case(sensor_props.get("scaleGroupName", ""))
if not (scale_name := enum_name_format(scale_name, remove_parenthesis_)):
scale_name = sensor_name
scales.setdefault(scale_name, {}).update(normalize_scale_definition(scale_def))
sensors[sensor_name]["scale"] = scale_name
scales = dict(sorted(scales.items(), key=lambda kv: kv[0]))
sensors = dict(sorted(sensors.items(), key=lambda kv: kv[0]))
def generate_int_enum_class_definition(
class_name: str,
enum_map: Mapping[str, int | str | dict],
enum_ref_url: str | None = None,
get_id_func: Callable | None = None,
docstring_info: str = "",
base_class: str = "IntEnum",
) -> list[str]:
"""Generate an IntEnum class definition as an array of lines of string."""
class_def: list[str] = []
class_def.append(f"class {class_name}({base_class}):")
docstring = (
f'"""Enum for known {docstring_info} multilevel sensor types."""'.replace(
" ", " "
)
)
class_def.append(f" {docstring}")
if enum_ref_url:
class_def.append(f" # {enum_ref_url}")
for enum_name, enum_id in enum_map.items():
if get_id_func:
enum_id = get_id_func(enum_id)
class_def.append(f" {enum_name} = {enum_id}")
return class_def
def generate_int_enum_base_class(class_name: str, docstring: str) -> list[str]:
"""Generate an IntEnum base class definition."""
class_def: list[str] = []
class_def.append(f"class {class_name}(IntEnum):")
class_def.append(f"\t{docstring}")
return class_def
SENSOR_TYPE_URL = get_registry_location("SensorTypes.ts")
lines = [
'"""Constants for the Multilevel Sensor CC."""',
*AUTO_GEN_PRE,
"from __future__ import annotations",
"",
"from enum import IntEnum",
'CC_SPECIFIC_SCALE = "scale"',
'CC_SPECIFIC_SENSOR_TYPE = "sensorType"',
"",
]
lines.extend(
[f"{sensor}_PROPERTY = \"{data['label']}\"" for sensor, data in sensors.items()]
)
lines.extend(
generate_int_enum_class_definition(
"MultilevelSensorType",
sensors,
SENSOR_TYPE_URL,
get_id_func=lambda x: x["id"],
)
)
lines.extend(
generate_int_enum_base_class(
"MultilevelSensorScaleType",
docstring='"""Common base class for multilevel sensor scale enums."""',
)
)
_unit_name_to_enum_map = defaultdict(list)
for scale_name, scale_dict in scales.items():
lines.extend(
generate_int_enum_class_definition(
format_for_class_name(scale_name, "Scale"),
scale_dict,
SENSOR_TYPE_URL,
docstring_info=f"scales for {scale_name}",
base_class="MultilevelSensorScaleType",
)
)
for unit_name in scale_dict.keys():
_unit_name_to_enum_map[unit_name].append(
f"{format_for_class_name(scale_name, 'Scale')}.{unit_name}"
)
unit_name_to_enum_map = dict(
sorted(_unit_name_to_enum_map.items(), key=lambda kv: kv[0])
)
for unit_name, enum_list in unit_name_to_enum_map.items():
unit_name_to_enum_map[unit_name] = sorted(enum_list)
multilevel_sensor_type_to_scale_map_line = (
"MULTILEVEL_SENSOR_TYPE_TO_SCALE_MAP: dict[MultilevelSensorType, "
"type[MultilevelSensorScaleType]] = {"
)
for sensor_name, sensor_def in sensors.items():
multilevel_sensor_type_to_scale_map_line += (
f" MultilevelSensorType.{sensor_name}: "
f"{format_for_class_name(sensor_def['scale'], 'Scale')},"
)
multilevel_sensor_type_to_scale_map_line += "}"
lines.append(multilevel_sensor_type_to_scale_map_line)
lines.append("")
for unit_name, unit_enums in unit_name_to_enum_map.items():
lines.append(
f"UNIT_{unit_name}: list[MultilevelSensorScaleType] = [{','.join(sorted(unit_enums))}]"
)
lines.extend(AUTO_GEN_POST)
lines.extend(get_manually_written_code(CONST_FILE_PATH))
CONST_FILE_PATH.write_text("\n".join(lines), encoding="utf-8")
run_black(CONST_FILE_PATH)
|