Add Xiaomi-ble remotes and dimmers (#109327)
This commit is contained in:
parent
3def42726a
commit
a95a51da05
11 changed files with 324 additions and 5 deletions
|
@ -29,6 +29,10 @@ from .coordinator import (
|
|||
from .device import device_key_to_bluetooth_entity_key
|
||||
|
||||
BINARY_SENSOR_DESCRIPTIONS = {
|
||||
XiaomiBinarySensorDeviceClass.BATTERY: BinarySensorEntityDescription(
|
||||
key=XiaomiBinarySensorDeviceClass.BATTERY,
|
||||
device_class=BinarySensorDeviceClass.BATTERY,
|
||||
),
|
||||
XiaomiBinarySensorDeviceClass.DOOR: BinarySensorEntityDescription(
|
||||
key=XiaomiBinarySensorDeviceClass.DOOR,
|
||||
device_class=BinarySensorDeviceClass.DOOR,
|
||||
|
@ -49,6 +53,10 @@ BINARY_SENSOR_DESCRIPTIONS = {
|
|||
key=XiaomiBinarySensorDeviceClass.OPENING,
|
||||
device_class=BinarySensorDeviceClass.OPENING,
|
||||
),
|
||||
XiaomiBinarySensorDeviceClass.POWER: BinarySensorEntityDescription(
|
||||
key=XiaomiBinarySensorDeviceClass.POWER,
|
||||
device_class=BinarySensorDeviceClass.POWER,
|
||||
),
|
||||
XiaomiBinarySensorDeviceClass.SMOKE: BinarySensorEntityDescription(
|
||||
key=XiaomiBinarySensorDeviceClass.SMOKE,
|
||||
device_class=BinarySensorDeviceClass.SMOKE,
|
||||
|
|
|
@ -19,14 +19,23 @@ EVENT_PROPERTIES: Final = "event_properties"
|
|||
XIAOMI_BLE_EVENT: Final = "xiaomi_ble_event"
|
||||
|
||||
EVENT_CLASS_BUTTON: Final = "button"
|
||||
EVENT_CLASS_DIMMER: Final = "dimmer"
|
||||
EVENT_CLASS_MOTION: Final = "motion"
|
||||
EVENT_CLASS_CUBE: Final = "cube"
|
||||
|
||||
BUTTON: Final = "button"
|
||||
CUBE: Final = "cube"
|
||||
DIMMER: Final = "dimmer"
|
||||
DOUBLE_BUTTON: Final = "double_button"
|
||||
TRIPPLE_BUTTON: Final = "tripple_button"
|
||||
REMOTE: Final = "remote"
|
||||
REMOTE_FAN: Final = "remote_fan"
|
||||
REMOTE_VENFAN: Final = "remote_ventilator_fan"
|
||||
REMOTE_BATHROOM: Final = "remote_bathroom"
|
||||
MOTION: Final = "motion"
|
||||
|
||||
BUTTON_PRESS: Final = "button_press"
|
||||
BUTTON_PRESS_LONG: Final = "button_press_long"
|
||||
BUTTON_PRESS_DOUBLE_LONG: Final = "button_press_double_long"
|
||||
DOUBLE_BUTTON_PRESS_DOUBLE_LONG: Final = "double_button_press_double_long"
|
||||
TRIPPLE_BUTTON_PRESS_DOUBLE_LONG: Final = "tripple_button_press_double_long"
|
||||
|
|
|
@ -24,16 +24,25 @@ from .const import (
|
|||
BUTTON,
|
||||
BUTTON_PRESS,
|
||||
BUTTON_PRESS_DOUBLE_LONG,
|
||||
BUTTON_PRESS_LONG,
|
||||
CONF_SUBTYPE,
|
||||
CUBE,
|
||||
DIMMER,
|
||||
DOMAIN,
|
||||
DOUBLE_BUTTON,
|
||||
DOUBLE_BUTTON_PRESS_DOUBLE_LONG,
|
||||
EVENT_CLASS,
|
||||
EVENT_CLASS_BUTTON,
|
||||
EVENT_CLASS_CUBE,
|
||||
EVENT_CLASS_DIMMER,
|
||||
EVENT_CLASS_MOTION,
|
||||
EVENT_TYPE,
|
||||
MOTION,
|
||||
MOTION_DEVICE,
|
||||
REMOTE,
|
||||
REMOTE_BATHROOM,
|
||||
REMOTE_FAN,
|
||||
REMOTE_VENFAN,
|
||||
TRIPPLE_BUTTON,
|
||||
TRIPPLE_BUTTON_PRESS_DOUBLE_LONG,
|
||||
XIAOMI_BLE_EVENT,
|
||||
|
@ -41,14 +50,61 @@ from .const import (
|
|||
|
||||
TRIGGERS_BY_TYPE = {
|
||||
BUTTON_PRESS: ["press"],
|
||||
BUTTON_PRESS_LONG: ["press", "long_press"],
|
||||
BUTTON_PRESS_DOUBLE_LONG: ["press", "double_press", "long_press"],
|
||||
CUBE: ["rotate_left", "rotate_right"],
|
||||
DIMMER: [
|
||||
"press",
|
||||
"long_press",
|
||||
"rotate_left",
|
||||
"rotate_right",
|
||||
"rotate_left_pressed",
|
||||
"rotate_right_pressed",
|
||||
],
|
||||
MOTION_DEVICE: ["motion_detected"],
|
||||
}
|
||||
|
||||
EVENT_TYPES = {
|
||||
BUTTON: ["button"],
|
||||
CUBE: ["cube"],
|
||||
DIMMER: ["dimmer"],
|
||||
DOUBLE_BUTTON: ["button_left", "button_right"],
|
||||
TRIPPLE_BUTTON: ["button_left", "button_middle", "button_right"],
|
||||
REMOTE: [
|
||||
"button_on",
|
||||
"button_off",
|
||||
"button_brightness",
|
||||
"button_plus",
|
||||
"button_min",
|
||||
"button_m",
|
||||
],
|
||||
REMOTE_BATHROOM: [
|
||||
"button_heat",
|
||||
"button_air_exchange",
|
||||
"button_dry",
|
||||
"button_fan",
|
||||
"button_swing",
|
||||
"button_decrease_speed",
|
||||
"button_increase_speed",
|
||||
"button_stop",
|
||||
"button_light",
|
||||
],
|
||||
REMOTE_FAN: [
|
||||
"button_fan",
|
||||
"button_light",
|
||||
"button_wind_speed",
|
||||
"button_wind_mode",
|
||||
"button_brightness",
|
||||
"button_color_temperature",
|
||||
],
|
||||
REMOTE_VENFAN: [
|
||||
"button_swing",
|
||||
"button_power",
|
||||
"button_timer_30_minutes",
|
||||
"button_timer_60_minutes",
|
||||
"button_increase_wind_speed",
|
||||
"button_decrease_wind_speed",
|
||||
],
|
||||
MOTION: ["motion"],
|
||||
}
|
||||
|
||||
|
@ -78,11 +134,41 @@ TRIGGER_MODEL_DATA = {
|
|||
event_types=EVENT_TYPES[DOUBLE_BUTTON],
|
||||
triggers=TRIGGERS_BY_TYPE[BUTTON_PRESS_DOUBLE_LONG],
|
||||
),
|
||||
CUBE: TriggerModelData(
|
||||
event_class=EVENT_CLASS_CUBE,
|
||||
event_types=EVENT_TYPES[CUBE],
|
||||
triggers=TRIGGERS_BY_TYPE[CUBE],
|
||||
),
|
||||
DIMMER: TriggerModelData(
|
||||
event_class=EVENT_CLASS_DIMMER,
|
||||
event_types=EVENT_TYPES[DIMMER],
|
||||
triggers=TRIGGERS_BY_TYPE[DIMMER],
|
||||
),
|
||||
TRIPPLE_BUTTON_PRESS_DOUBLE_LONG: TriggerModelData(
|
||||
event_class=EVENT_CLASS_BUTTON,
|
||||
event_types=EVENT_TYPES[TRIPPLE_BUTTON],
|
||||
triggers=TRIGGERS_BY_TYPE[BUTTON_PRESS_DOUBLE_LONG],
|
||||
),
|
||||
REMOTE: TriggerModelData(
|
||||
event_class=EVENT_CLASS_BUTTON,
|
||||
event_types=EVENT_TYPES[REMOTE],
|
||||
triggers=TRIGGERS_BY_TYPE[BUTTON_PRESS_LONG],
|
||||
),
|
||||
REMOTE_BATHROOM: TriggerModelData(
|
||||
event_class=EVENT_CLASS_BUTTON,
|
||||
event_types=EVENT_TYPES[REMOTE_BATHROOM],
|
||||
triggers=TRIGGERS_BY_TYPE[BUTTON_PRESS_LONG],
|
||||
),
|
||||
REMOTE_FAN: TriggerModelData(
|
||||
event_class=EVENT_CLASS_BUTTON,
|
||||
event_types=EVENT_TYPES[REMOTE_FAN],
|
||||
triggers=TRIGGERS_BY_TYPE[BUTTON_PRESS_LONG],
|
||||
),
|
||||
REMOTE_VENFAN: TriggerModelData(
|
||||
event_class=EVENT_CLASS_BUTTON,
|
||||
event_types=EVENT_TYPES[REMOTE_VENFAN],
|
||||
triggers=TRIGGERS_BY_TYPE[BUTTON_PRESS_LONG],
|
||||
),
|
||||
MOTION_DEVICE: TriggerModelData(
|
||||
event_class=EVENT_CLASS_MOTION,
|
||||
event_types=EVENT_TYPES[MOTION],
|
||||
|
@ -103,7 +189,13 @@ MODEL_DATA = {
|
|||
"XMWXKG01YL": TRIGGER_MODEL_DATA[DOUBLE_BUTTON_PRESS_DOUBLE_LONG],
|
||||
"K9B-2BTN": TRIGGER_MODEL_DATA[DOUBLE_BUTTON_PRESS_DOUBLE_LONG],
|
||||
"K9B-3BTN": TRIGGER_MODEL_DATA[TRIPPLE_BUTTON_PRESS_DOUBLE_LONG],
|
||||
"YLYK01YL": TRIGGER_MODEL_DATA[REMOTE],
|
||||
"YLYK01YL-FANRC": TRIGGER_MODEL_DATA[REMOTE_FAN],
|
||||
"YLYK01YL-VENFAN": TRIGGER_MODEL_DATA[REMOTE_VENFAN],
|
||||
"YLYK01YL-BHFRC": TRIGGER_MODEL_DATA[REMOTE_BATHROOM],
|
||||
"MUE4094RT": TRIGGER_MODEL_DATA[MOTION_DEVICE],
|
||||
"XMMF01JQD": TRIGGER_MODEL_DATA[CUBE],
|
||||
"YLKG07YL/YLKG08YL": TRIGGER_MODEL_DATA[DIMMER],
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ from . import format_discovered_event_class, format_event_dispatcher_name
|
|||
from .const import (
|
||||
DOMAIN,
|
||||
EVENT_CLASS_BUTTON,
|
||||
EVENT_CLASS_CUBE,
|
||||
EVENT_CLASS_DIMMER,
|
||||
EVENT_CLASS_MOTION,
|
||||
EVENT_PROPERTIES,
|
||||
EVENT_TYPE,
|
||||
|
@ -36,10 +38,31 @@ DESCRIPTIONS_BY_EVENT_CLASS = {
|
|||
],
|
||||
device_class=EventDeviceClass.BUTTON,
|
||||
),
|
||||
EVENT_CLASS_CUBE: EventEntityDescription(
|
||||
key=EVENT_CLASS_CUBE,
|
||||
translation_key="cube",
|
||||
event_types=[
|
||||
"rotate_left",
|
||||
"rotate_right",
|
||||
],
|
||||
),
|
||||
EVENT_CLASS_DIMMER: EventEntityDescription(
|
||||
key=EVENT_CLASS_DIMMER,
|
||||
translation_key="dimmer",
|
||||
event_types=[
|
||||
"press",
|
||||
"long_press",
|
||||
"rotate_left",
|
||||
"rotate_right",
|
||||
"rotate_left_pressed",
|
||||
"rotate_right_pressed",
|
||||
],
|
||||
),
|
||||
EVENT_CLASS_MOTION: EventEntityDescription(
|
||||
key=EVENT_CLASS_MOTION,
|
||||
translation_key="motion",
|
||||
event_types=["motion_detected"],
|
||||
device_class=EventDeviceClass.MOTION,
|
||||
),
|
||||
}
|
||||
|
||||
|
|
|
@ -24,5 +24,5 @@
|
|||
"dependencies": ["bluetooth_adapters"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/xiaomi_ble",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["xiaomi-ble==0.23.1"]
|
||||
"requirements": ["xiaomi-ble==0.25.2"]
|
||||
}
|
||||
|
|
|
@ -44,14 +44,43 @@
|
|||
"press": "Press",
|
||||
"double_press": "Double Press",
|
||||
"long_press": "Long Press",
|
||||
"motion_detected": "Motion Detected"
|
||||
"motion_detected": "Motion Detected",
|
||||
"rotate_left": "Rotate Left",
|
||||
"rotate_right": "Rotate Right",
|
||||
"rotate_left_pressed": "Rotate Left (Pressed)",
|
||||
"rotate_right_pressed": "Rotate Right (Pressed)"
|
||||
},
|
||||
"trigger_type": {
|
||||
"button": "Button \"{subtype}\"",
|
||||
"button_left": "Button Left \"{subtype}\"",
|
||||
"button_middle": "Button Middle \"{subtype}\"",
|
||||
"button_right": "Button Right \"{subtype}\"",
|
||||
"motion": "{subtype}"
|
||||
"button_on": "Button On \"{subtype}\"",
|
||||
"button_off": "Button Off \"{subtype}\"",
|
||||
"button_brightness": "Button Brightness \"{subtype}\"",
|
||||
"button_plus": "Button Plus \"{subtype}\"",
|
||||
"button_min": "Button Min \"{subtype}\"",
|
||||
"button_m": "Button M \"{subtype}\"",
|
||||
"button_heat": "Button Heat \"{subtype}\"",
|
||||
"button_air_exchange": "Button Air Exchange \"{subtype}\"",
|
||||
"button_dry": "Button Dry \"{subtype}\"",
|
||||
"button_fan": "Button Fan \"{subtype}\"",
|
||||
"button_swing": "Button Swing \"{subtype}\"",
|
||||
"button_decrease_speed": "Button Decrease Speed \"{subtype}\"",
|
||||
"button_increase_speed": "Button Inrease Speed \"{subtype}\"",
|
||||
"button_stop": "Button Stop \"{subtype}\"",
|
||||
"button_light": "Button Light \"{subtype}\"",
|
||||
"button_wind_speed": "Button Wind Speed \"{subtype}\"",
|
||||
"button_wind_mode": "Button Wind Mode \"{subtype}\"",
|
||||
"button_color_temperature": "Button Color Temperature \"{subtype}\"",
|
||||
"button_power": "Button Power \"{subtype}\"",
|
||||
"button_timer_30_minutes": "Button Timer 30 Minutes \"{subtype}\"",
|
||||
"button_timer_60_minutes": "Button Timer 30 Minutes \"{subtype}\"",
|
||||
"button_increase_wind_speed": "Button Increase Wind Speed \"{subtype}\"",
|
||||
"button_decrease_wind_speed": "Button Decrease Wind Speed \"{subtype}\"",
|
||||
"dimmer": "{subtype}",
|
||||
"motion": "{subtype}",
|
||||
"cube": "{subtype}"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
|
@ -67,6 +96,30 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"cube": {
|
||||
"state_attributes": {
|
||||
"event_type": {
|
||||
"state": {
|
||||
"rotate_left": "Rotate left",
|
||||
"rotate_right": "Rotate right"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dimmer": {
|
||||
"state_attributes": {
|
||||
"event_type": {
|
||||
"state": {
|
||||
"press": "Press",
|
||||
"long_press": "Long press",
|
||||
"rotate_left": "Rotate left",
|
||||
"rotate_right": "Rotate right",
|
||||
"rotate_left_pressed": "Rotate left (pressed)",
|
||||
"rotate_right_pressed": "Rotate left (pressed)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"motion": {
|
||||
"state_attributes": {
|
||||
"event_type": {
|
||||
|
|
|
@ -2853,7 +2853,7 @@ wyoming==1.5.2
|
|||
xbox-webapi==2.0.11
|
||||
|
||||
# homeassistant.components.xiaomi_ble
|
||||
xiaomi-ble==0.23.1
|
||||
xiaomi-ble==0.25.2
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknx==2.11.2
|
||||
|
|
|
@ -2182,7 +2182,7 @@ wyoming==1.5.2
|
|||
xbox-webapi==2.0.11
|
||||
|
||||
# homeassistant.components.xiaomi_ble
|
||||
xiaomi-ble==0.23.1
|
||||
xiaomi-ble==0.25.2
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknx==2.11.2
|
||||
|
|
|
@ -262,6 +262,37 @@ async def test_smoke(hass: HomeAssistant) -> None:
|
|||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_power(hass: HomeAssistant) -> None:
|
||||
"""Test setting up a power binary sensor."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="F8:24:41:E9:50:74",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(hass.states.async_all()) == 0
|
||||
inject_bluetooth_service_info_bleak(
|
||||
hass,
|
||||
make_advertisement(
|
||||
"F8:24:41:E9:50:74",
|
||||
b"P0S\x01?tP\xe9A$\xf8\x01\x10\x03\x01\x00\x00",
|
||||
),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_all()) == 2
|
||||
|
||||
power_sensor = hass.states.get("binary_sensor.remote_control_5074_power")
|
||||
power_sensor_attribtes = power_sensor.attributes
|
||||
assert power_sensor.state == STATE_OFF
|
||||
assert power_sensor_attribtes[ATTR_FRIENDLY_NAME] == "Remote Control 5074 Power"
|
||||
|
||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_unavailable(hass: HomeAssistant) -> None:
|
||||
"""Test normal device goes to unavailable after 60 minutes."""
|
||||
start_monotonic = time.monotonic()
|
||||
|
|
|
@ -97,6 +97,32 @@ async def test_event_motion_detected(hass: HomeAssistant) -> None:
|
|||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_event_dimmer_rotate(hass: HomeAssistant) -> None:
|
||||
"""Make sure that a dimmer rotate event is fired."""
|
||||
mac = "F8:24:41:C5:98:8B"
|
||||
data = {"bindkey": "b853075158487ca39a5b5ea9"}
|
||||
entry = await _async_setup_xiaomi_device(hass, mac, data)
|
||||
events = async_capture_events(hass, "xiaomi_ble_event")
|
||||
|
||||
# Emit dimmer rotate left with 3 steps event
|
||||
inject_bluetooth_service_info_bleak(
|
||||
hass,
|
||||
make_advertisement(
|
||||
mac, b"X0\xb6\x036\x8b\x98\xc5A$\xf8\x8b\xb8\xf2f" b"\x13Q\x00\x00\x00\xd6"
|
||||
),
|
||||
)
|
||||
|
||||
# wait for the event
|
||||
await hass.async_block_till_done()
|
||||
assert len(events) == 1
|
||||
assert events[0].data["address"] == "F8:24:41:C5:98:8B"
|
||||
assert events[0].data["event_type"] == "rotate_left"
|
||||
assert events[0].data["event_properties"] == {"steps": 1}
|
||||
|
||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_get_triggers_button(hass: HomeAssistant) -> None:
|
||||
"""Test that we get the expected triggers from a Xiaomi BLE button sensor."""
|
||||
mac = "54:EF:44:E3:9C:BC"
|
||||
|
|
|
@ -49,6 +49,36 @@ from tests.components.bluetooth import (
|
|||
}
|
||||
],
|
||||
),
|
||||
(
|
||||
"F8:24:41:E9:50:74",
|
||||
make_advertisement(
|
||||
"F8:24:41:E9:50:74",
|
||||
b"P0S\x01?tP\xe9A$\xf8\x01\x10\x03\x04\x00\x02",
|
||||
),
|
||||
None,
|
||||
[
|
||||
{
|
||||
"entity": "event.remote_control_5074_button_m",
|
||||
ATTR_FRIENDLY_NAME: "Remote Control 5074 Button M",
|
||||
ATTR_EVENT_TYPE: "long_press",
|
||||
}
|
||||
],
|
||||
),
|
||||
(
|
||||
"F8:24:41:E9:50:74",
|
||||
make_advertisement(
|
||||
"F8:24:41:E9:50:74",
|
||||
b"P0S\x01?tP\xe9A$\xf8\x01\x10\x03\x03\x00\x00",
|
||||
),
|
||||
None,
|
||||
[
|
||||
{
|
||||
"entity": "event.remote_control_5074_button_plus",
|
||||
ATTR_FRIENDLY_NAME: "Remote Control 5074 Button plus",
|
||||
ATTR_EVENT_TYPE: "press",
|
||||
}
|
||||
],
|
||||
),
|
||||
(
|
||||
"DE:70:E8:B2:39:0C",
|
||||
make_advertisement(
|
||||
|
@ -64,6 +94,53 @@ from tests.components.bluetooth import (
|
|||
}
|
||||
],
|
||||
),
|
||||
(
|
||||
"E2:53:30:E6:D3:54",
|
||||
make_advertisement(
|
||||
"E2:53:30:E6:D3:54",
|
||||
b"P0\xe1\x04\x8eT\xd3\xe60S\xe2\x01\x10\x03\x01\x00\x00",
|
||||
),
|
||||
None,
|
||||
[
|
||||
{
|
||||
"entity": "event.magic_cube_d354_cube",
|
||||
ATTR_FRIENDLY_NAME: "Magic Cube D354 Cube",
|
||||
ATTR_EVENT_TYPE: "rotate_left",
|
||||
}
|
||||
],
|
||||
),
|
||||
(
|
||||
"F8:24:41:C5:98:8B",
|
||||
make_advertisement(
|
||||
"F8:24:41:C5:98:8B",
|
||||
b"X0\xb6\x036\x8b\x98\xc5A$\xf8\x8b\xb8\xf2f" b"\x13Q\x00\x00\x00\xd6",
|
||||
),
|
||||
"b853075158487ca39a5b5ea9",
|
||||
[
|
||||
{
|
||||
"entity": "event.dimmer_switch_988b_dimmer",
|
||||
ATTR_FRIENDLY_NAME: "Dimmer Switch 988B Dimmer",
|
||||
ATTR_EVENT_TYPE: "rotate_left",
|
||||
"event_properties": {"steps": 1},
|
||||
}
|
||||
],
|
||||
),
|
||||
(
|
||||
"F8:24:41:C5:98:8B",
|
||||
make_advertisement(
|
||||
"F8:24:41:C5:98:8B",
|
||||
b"X0\xb6\x03\xd2\x8b\x98\xc5A$\xf8\xc3I\x14vu~\x00\x00\x00\x99",
|
||||
),
|
||||
"b853075158487ca39a5b5ea9",
|
||||
[
|
||||
{
|
||||
"entity": "event.dimmer_switch_988b_dimmer",
|
||||
ATTR_FRIENDLY_NAME: "Dimmer Switch 988B Dimmer",
|
||||
ATTR_EVENT_TYPE: "press",
|
||||
"event_properties": {"duration": 2},
|
||||
}
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_events(
|
||||
|
|
Loading…
Add table
Reference in a new issue