mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Add support for Energy Production CC sensors (#93839)
This commit is contained in:
parent
c72477811e
commit
1eb1ea08b0
@ -133,6 +133,11 @@ ENTITY_DESC_KEY_TARGET_TEMPERATURE = "target_temperature"
|
||||
ENTITY_DESC_KEY_MEASUREMENT = "measurement"
|
||||
ENTITY_DESC_KEY_TOTAL_INCREASING = "total_increasing"
|
||||
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_POWER = "energy_production_power"
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TIME = "energy_production_time"
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TOTAL = "energy_production_total"
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TODAY = "energy_production_today"
|
||||
|
||||
# This API key is only for use with Home Assistant. Reach out to Z-Wave JS to apply for
|
||||
# your own (https://github.com/zwave-js/firmware-updates/).
|
||||
API_KEY_FIRMWARE_UPDATE_SERVICE = (
|
||||
|
@ -722,9 +722,10 @@ DISCOVERY_SCHEMAS = [
|
||||
hint="numeric_sensor",
|
||||
primary_value=ZWaveValueDiscoverySchema(
|
||||
command_class={
|
||||
CommandClass.SENSOR_MULTILEVEL,
|
||||
CommandClass.SENSOR_ALARM,
|
||||
CommandClass.BATTERY,
|
||||
CommandClass.ENERGY_PRODUCTION,
|
||||
CommandClass.SENSOR_ALARM,
|
||||
CommandClass.SENSOR_MULTILEVEL,
|
||||
},
|
||||
type={ValueType.NUMBER},
|
||||
),
|
||||
|
@ -7,6 +7,14 @@ import logging
|
||||
from typing import Any, cast
|
||||
|
||||
from zwave_js_server.const import CommandClass
|
||||
from zwave_js_server.const.command_class.energy_production import (
|
||||
EnergyProductionParameter,
|
||||
EnergyProductionScaleType,
|
||||
PowerScale,
|
||||
TodaysProductionScale,
|
||||
TotalProductionScale,
|
||||
TotalTimeScale,
|
||||
)
|
||||
from zwave_js_server.const.command_class.meter import (
|
||||
CURRENT_METER_TYPES,
|
||||
ENERGY_TOTAL_INCREASING_METER_TYPES,
|
||||
@ -85,6 +93,10 @@ from zwave_js_server.model.value import (
|
||||
Value as ZwaveValue,
|
||||
get_value_id_str,
|
||||
)
|
||||
from zwave_js_server.util.command_class.energy_production import (
|
||||
get_energy_production_parameter,
|
||||
get_energy_production_scale_type,
|
||||
)
|
||||
from zwave_js_server.util.command_class.meter import get_meter_scale_type
|
||||
from zwave_js_server.util.command_class.multilevel_sensor import (
|
||||
get_multilevel_sensor_scale_type,
|
||||
@ -123,6 +135,10 @@ from .const import (
|
||||
ENTITY_DESC_KEY_CO2,
|
||||
ENTITY_DESC_KEY_CURRENT,
|
||||
ENTITY_DESC_KEY_ENERGY_MEASUREMENT,
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_POWER,
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TIME,
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TODAY,
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TOTAL,
|
||||
ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING,
|
||||
ENTITY_DESC_KEY_HUMIDITY,
|
||||
ENTITY_DESC_KEY_ILLUMINANCE,
|
||||
@ -138,6 +154,18 @@ from .const import (
|
||||
)
|
||||
from .helpers import ZwaveValueID
|
||||
|
||||
ENERGY_PRODUCTION_DEVICE_CLASS_MAP: dict[str, list[EnergyProductionParameter]] = {
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TIME: [EnergyProductionParameter.TOTAL_TIME],
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TODAY: [
|
||||
EnergyProductionParameter.TODAYS_PRODUCTION
|
||||
],
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TOTAL: [
|
||||
EnergyProductionParameter.TOTAL_PRODUCTION
|
||||
],
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_POWER: [EnergyProductionParameter.POWER],
|
||||
}
|
||||
|
||||
|
||||
METER_DEVICE_CLASS_MAP: dict[str, list[MeterScaleType]] = {
|
||||
ENTITY_DESC_KEY_CURRENT: CURRENT_METER_TYPES,
|
||||
ENTITY_DESC_KEY_VOLTAGE: VOLTAGE_METER_TYPES,
|
||||
@ -160,6 +188,16 @@ MULTILEVEL_SENSOR_DEVICE_CLASS_MAP: dict[str, list[MultilevelSensorType]] = {
|
||||
ENTITY_DESC_KEY_VOLTAGE: VOLTAGE_SENSORS,
|
||||
}
|
||||
|
||||
ENERGY_PRODUCTION_UNIT_MAP: dict[str, list[EnergyProductionScaleType]] = {
|
||||
UnitOfEnergy.WATT_HOUR: [
|
||||
TotalProductionScale.WATT_HOURS,
|
||||
TodaysProductionScale.WATT_HOURS,
|
||||
],
|
||||
UnitOfPower.WATT: [PowerScale.WATTS],
|
||||
UnitOfTime.SECONDS: [TotalTimeScale.SECONDS],
|
||||
UnitOfTime.HOURS: [TotalTimeScale.HOURS],
|
||||
}
|
||||
|
||||
METER_UNIT_MAP: dict[str, list[MeterScaleType]] = {
|
||||
UnitOfElectricCurrent.AMPERE: METER_UNIT_AMPERE,
|
||||
UnitOfVolume.CUBIC_FEET: UNIT_CUBIC_FEET,
|
||||
@ -320,12 +358,18 @@ class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate):
|
||||
|
||||
@staticmethod
|
||||
def find_key_from_matching_set(
|
||||
enum_value: MultilevelSensorType | MultilevelSensorScaleType | MeterScaleType,
|
||||
enum_value: MultilevelSensorType
|
||||
| MultilevelSensorScaleType
|
||||
| MeterScaleType
|
||||
| EnergyProductionParameter
|
||||
| EnergyProductionScaleType,
|
||||
set_map: Mapping[
|
||||
str,
|
||||
list[MultilevelSensorType]
|
||||
| list[MultilevelSensorScaleType]
|
||||
| list[MeterScaleType],
|
||||
| list[MeterScaleType]
|
||||
| list[EnergyProductionScaleType]
|
||||
| list[EnergyProductionParameter],
|
||||
],
|
||||
) -> str | None:
|
||||
"""Find a key in a set map that matches a given enum value."""
|
||||
@ -387,6 +431,18 @@ class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate):
|
||||
if key:
|
||||
return NumericSensorDataTemplateData(key, unit)
|
||||
|
||||
if value.command_class == CommandClass.ENERGY_PRODUCTION:
|
||||
energy_production_parameter = get_energy_production_parameter(value)
|
||||
energy_production_scale_type = get_energy_production_scale_type(value)
|
||||
unit = self.find_key_from_matching_set(
|
||||
energy_production_scale_type, ENERGY_PRODUCTION_UNIT_MAP
|
||||
)
|
||||
key = self.find_key_from_matching_set(
|
||||
energy_production_parameter, ENERGY_PRODUCTION_DEVICE_CLASS_MAP
|
||||
)
|
||||
if key:
|
||||
return NumericSensorDataTemplateData(key, unit)
|
||||
|
||||
return NumericSensorDataTemplateData()
|
||||
|
||||
|
||||
|
@ -58,6 +58,10 @@ from .const import (
|
||||
ENTITY_DESC_KEY_CO2,
|
||||
ENTITY_DESC_KEY_CURRENT,
|
||||
ENTITY_DESC_KEY_ENERGY_MEASUREMENT,
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_POWER,
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TIME,
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TODAY,
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TOTAL,
|
||||
ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING,
|
||||
ENTITY_DESC_KEY_HUMIDITY,
|
||||
ENTITY_DESC_KEY_ILLUMINANCE,
|
||||
@ -235,6 +239,50 @@ ENTITY_DESCRIPTION_KEY_DEVICE_CLASS_MAP: dict[
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
|
||||
),
|
||||
(
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TIME,
|
||||
UnitOfTime.SECONDS,
|
||||
): SensorEntityDescription(
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TIME,
|
||||
name="Energy production time",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
),
|
||||
(ENTITY_DESC_KEY_ENERGY_PRODUCTION_TIME, UnitOfTime.HOURS): SensorEntityDescription(
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TIME,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.HOURS,
|
||||
),
|
||||
(
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TODAY,
|
||||
UnitOfEnergy.WATT_HOUR,
|
||||
): SensorEntityDescription(
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TODAY,
|
||||
name="Energy production today",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
),
|
||||
(
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TOTAL,
|
||||
UnitOfEnergy.WATT_HOUR,
|
||||
): SensorEntityDescription(
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_TOTAL,
|
||||
name="Energy production total",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
),
|
||||
(
|
||||
ENTITY_DESC_KEY_ENERGY_PRODUCTION_POWER,
|
||||
UnitOfPower.WATT,
|
||||
): SensorEntityDescription(
|
||||
ENTITY_DESC_KEY_POWER,
|
||||
name="Energy production power",
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
),
|
||||
}
|
||||
|
||||
# These descriptions are without device class.
|
||||
@ -547,13 +595,14 @@ class ZwaveSensor(ZWaveBaseEntity, SensorEntity):
|
||||
unit_of_measurement: str | None = None,
|
||||
) -> None:
|
||||
"""Initialize a ZWaveSensorBase entity."""
|
||||
super().__init__(config_entry, driver, info)
|
||||
self.entity_description = entity_description
|
||||
super().__init__(config_entry, driver, info)
|
||||
self._attr_native_unit_of_measurement = unit_of_measurement
|
||||
|
||||
# Entity class attributes
|
||||
self._attr_force_update = True
|
||||
self._attr_name = self.generate_name(include_value_name=True)
|
||||
if not entity_description.name:
|
||||
self._attr_name = self.generate_name(include_value_name=True)
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
|
@ -624,6 +624,12 @@ def indicator_test_state_fixture():
|
||||
return json.loads(load_fixture("zwave_js/indicator_test_state.json"))
|
||||
|
||||
|
||||
@pytest.fixture(name="energy_production_state", scope="session")
|
||||
def energy_production_state_fixture():
|
||||
"""Load a mock node with energy production CC state fixture data."""
|
||||
return json.loads(load_fixture("zwave_js/energy_production_state.json"))
|
||||
|
||||
|
||||
# model fixtures
|
||||
|
||||
|
||||
@ -1191,3 +1197,11 @@ def indicator_test_fixture(client, indicator_test_state):
|
||||
node = Node(client, copy.deepcopy(indicator_test_state))
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="energy_production")
|
||||
def energy_prodution_fixture(client, energy_production_state):
|
||||
"""Mock a mock node with Energy Production CC."""
|
||||
node = Node(client, copy.deepcopy(energy_production_state))
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
241
tests/components/zwave_js/fixtures/energy_production_state.json
Normal file
241
tests/components/zwave_js/fixtures/energy_production_state.json
Normal file
@ -0,0 +1,241 @@
|
||||
{
|
||||
"nodeId": 2,
|
||||
"index": 0,
|
||||
"status": 4,
|
||||
"ready": true,
|
||||
"isListening": true,
|
||||
"isRouting": true,
|
||||
"isSecure": false,
|
||||
"interviewAttempts": 1,
|
||||
"endpoints": [
|
||||
{
|
||||
"nodeId": 2,
|
||||
"index": 0,
|
||||
"deviceClass": {
|
||||
"basic": {
|
||||
"key": 4,
|
||||
"label": "Routing Slave"
|
||||
},
|
||||
"generic": {
|
||||
"key": 6,
|
||||
"label": "Appliance"
|
||||
},
|
||||
"specific": {
|
||||
"key": 1,
|
||||
"label": "General Appliance"
|
||||
},
|
||||
"mandatorySupportedCCs": [],
|
||||
"mandatoryControlledCCs": []
|
||||
},
|
||||
"commandClasses": [
|
||||
{
|
||||
"id": 134,
|
||||
"name": "Version",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 144,
|
||||
"name": "Energy Production",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"values": [
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "firmwareVersions",
|
||||
"propertyName": "firmwareVersions",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "string[]",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave chip firmware versions",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "libraryType",
|
||||
"propertyName": "libraryType",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Library type",
|
||||
"states": {
|
||||
"0": "Unknown",
|
||||
"1": "Static Controller",
|
||||
"2": "Controller",
|
||||
"3": "Enhanced Slave",
|
||||
"4": "Slave",
|
||||
"5": "Installer",
|
||||
"6": "Routing Slave",
|
||||
"7": "Bridge Controller",
|
||||
"8": "Device under Test",
|
||||
"9": "N/A",
|
||||
"10": "AV Remote",
|
||||
"11": "AV Device"
|
||||
},
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "protocolVersion",
|
||||
"propertyName": "protocolVersion",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave protocol version",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 144,
|
||||
"commandClassName": "Energy Production",
|
||||
"property": "value",
|
||||
"propertyKey": 0,
|
||||
"propertyName": "value",
|
||||
"propertyKeyName": "0",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Power",
|
||||
"ccSpecific": {
|
||||
"parameter": 0,
|
||||
"scale": 0
|
||||
},
|
||||
"unit": "W",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": 1.23
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 144,
|
||||
"commandClassName": "Energy Production",
|
||||
"property": "value",
|
||||
"propertyKey": 1,
|
||||
"propertyName": "value",
|
||||
"propertyKeyName": "1",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Production Total",
|
||||
"ccSpecific": {
|
||||
"parameter": 1,
|
||||
"scale": 0
|
||||
},
|
||||
"unit": "Wh",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": 1234.56
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 144,
|
||||
"commandClassName": "Energy Production",
|
||||
"property": "value",
|
||||
"propertyKey": 2,
|
||||
"propertyName": "value",
|
||||
"propertyKeyName": "2",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Production Today",
|
||||
"ccSpecific": {
|
||||
"parameter": 2,
|
||||
"scale": 0
|
||||
},
|
||||
"unit": "Wh",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": 123.45
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 144,
|
||||
"commandClassName": "Energy Production",
|
||||
"property": "value",
|
||||
"propertyKey": 3,
|
||||
"propertyName": "value",
|
||||
"propertyKeyName": "3",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Total Time",
|
||||
"ccSpecific": {
|
||||
"parameter": 3,
|
||||
"scale": 0
|
||||
},
|
||||
"unit": "seconds",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": 123456
|
||||
}
|
||||
],
|
||||
"isFrequentListening": false,
|
||||
"maxDataRate": 100000,
|
||||
"supportedDataRates": [40000, 9600, 100000],
|
||||
"protocolVersion": 3,
|
||||
"supportsBeaming": true,
|
||||
"supportsSecurity": false,
|
||||
"nodeType": 1,
|
||||
"deviceClass": {
|
||||
"basic": {
|
||||
"key": 4,
|
||||
"label": "Routing Slave"
|
||||
},
|
||||
"generic": {
|
||||
"key": 6,
|
||||
"label": "Appliance"
|
||||
},
|
||||
"specific": {
|
||||
"key": 1,
|
||||
"label": "General Appliance"
|
||||
},
|
||||
"mandatorySupportedCCs": [],
|
||||
"mandatoryControlledCCs": []
|
||||
},
|
||||
"interviewStage": "Complete",
|
||||
"statistics": {
|
||||
"commandsTX": 10,
|
||||
"commandsRX": 7,
|
||||
"commandsDroppedRX": 0,
|
||||
"commandsDroppedTX": 0,
|
||||
"timeoutResponse": 1,
|
||||
"rtt": 84.8
|
||||
},
|
||||
"highestSecurityClass": -1,
|
||||
"isControllerNode": false,
|
||||
"keepAwake": false
|
||||
}
|
@ -35,6 +35,7 @@ from homeassistant.const import (
|
||||
UnitOfEnergy,
|
||||
UnitOfPower,
|
||||
UnitOfTemperature,
|
||||
UnitOfTime,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
@ -733,3 +734,54 @@ async def test_statistics_sensors(
|
||||
state = hass.states.get(f"{prefix}{suffix_key}")
|
||||
assert state
|
||||
assert state.state == str(val)
|
||||
|
||||
|
||||
ENERGY_PRODUCTION_ENTITY_MAP = {
|
||||
"energy_production_power": {
|
||||
"state": 1.23,
|
||||
"attributes": {
|
||||
"unit_of_measurement": UnitOfPower.WATT,
|
||||
"device_class": SensorDeviceClass.POWER,
|
||||
"state_class": SensorStateClass.MEASUREMENT,
|
||||
},
|
||||
},
|
||||
"energy_production_total": {
|
||||
"state": 1234.56,
|
||||
"attributes": {
|
||||
"unit_of_measurement": UnitOfEnergy.WATT_HOUR,
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL_INCREASING,
|
||||
},
|
||||
},
|
||||
"energy_production_today": {
|
||||
"state": 123.45,
|
||||
"attributes": {
|
||||
"unit_of_measurement": UnitOfEnergy.WATT_HOUR,
|
||||
"device_class": SensorDeviceClass.ENERGY,
|
||||
"state_class": SensorStateClass.TOTAL_INCREASING,
|
||||
},
|
||||
},
|
||||
"energy_production_time": {
|
||||
"state": 123456.0,
|
||||
"attributes": {
|
||||
"unit_of_measurement": UnitOfTime.SECONDS,
|
||||
"device_class": SensorDeviceClass.DURATION,
|
||||
},
|
||||
"missing_attributes": ["state_class"],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async def test_energy_production_sensors(
|
||||
hass: HomeAssistant, energy_production, client, integration
|
||||
) -> None:
|
||||
"""Test sensors for Energy Production CC."""
|
||||
for entity_id_suffix, state_data in ENERGY_PRODUCTION_ENTITY_MAP.items():
|
||||
state = hass.states.get(f"sensor.node_2_{entity_id_suffix}")
|
||||
assert state
|
||||
assert state.state == str(state_data["state"])
|
||||
for attr, val in state_data["attributes"].items():
|
||||
assert state.attributes[attr] == val
|
||||
|
||||
for attr in state_data.get("missing_attributes", []):
|
||||
assert attr not in state.attributes
|
||||
|
Loading…
x
Reference in New Issue
Block a user